We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
现在面试经常遇到需要手写javascript原生方法的要求。最常见的如,手写bind、apply和call方法。这些题目的目的在于考察你对于javascript的理解能力。
bind
apply
call
实现call和apply类似,有几个要点:
this
我们知道函数执行中,this的指向有三种情况,1. 使用new构造一个实例 2. 使用构造一个属性为函数的对象,通过.预算符调用函数 3. 出以上两种外,默认this是全局对象,如window.那么分析可知只有在1和2里做选择了.构造函数方法没有普遍性,所以只能通过方法2了. 我们从参数中得到上下文环境context和要调用的函数fn,吧fn作为context中的属性,调用结束后删除调这个属性就可以了.
new
.
window
context
fn
有个问题是,如果`context`中已经存在了`fn`属性的话,会把原本的属性覆盖掉.可以额外再加一个属性的检测.
有了上下文和函数后, 之后就是如何调用了.使用eval函数,拼接一个函数的字符串传入进入就可以了.
eval
// text.call2(this, ....) Function.prototype.call2 = function () { var args = [] var context // 如果是基础类型的话,自动封一个包装类 if (context == null) { context = window } else { context = typeof context != 'object' ? Object(arguments[0]) : context } context.fn = this for(var i = 1;i < arguments.length;i++) args.push('arguments[' + i + ']') var result = eval('context.fn('+args+')') delete context.fn return result }
通过对arguments的浅复制, 有同学可能要问, 可以直接用Array.prototype.slice.call(arguments, 1)得到参数的列表吗?题要求是实现自己的call,然后在里面又用了call是不是很怪.所以就用嘴简单的办法遍历arguments复制到一个新的数组.得到了参数列表后,我们要在上面的eval中拼接参数, context.fn('+args+')里,数组会自动调用toString()方法,转换成带有,的字符串.
arguments
Array.prototype.slice.call(arguments, 1)
context.fn('+args+')
toString()
,
以上已经完成了90%的工作了,最后就剩下this类型处理的分析.如果this是null或者undefined则this为window.如果this是Object类型的话,以上的逻辑的没有问题的,如果是基础类型的怎么办呀?基础类型是不可以添加属性的,所以我们要加一个判断,如果是对象类型的话,就用这个对象,否则就把这个对象转换成对象类型.
null
undefined
apply和call类似
// text.call2(this, ....) Function.prototype.call2 = function (context, arr) { var args = [] // 如果是基础类型的话,自动封一个包装类 if (context == null) { context = window } else { context = typeof context != 'object' ? Object(arguments[0]) : context } context.fn = this for(var i = 1;i < arr.length;i++) args.push('arr[' + i + ']') var result = eval('context.fn('+args+')') delete context.fn return result }
// var fn = test.bind(this, 1,2,3) Function.prototype.bind2 = function () { var fn = this var context = arguments[0], preArgs = [] var args = arguments if (context == null) { context = window } else { context = typeof context != 'object' ? Object(arguments[0]) : context } for(var i = 1;i< args.length;i++) { preArgs.push('args[' + i + ']') } function fBound () { context.fn = fn var arr = preArgs for(var i = 0;i < arguments.length;i++) { arr.push('arguments[' + i + ']') } var result = eval('context.fn('+arr+')') delete context.fn return result } // 新生成的函数要继承之前函数 var noop = function() {} noop.prototype = fn.prototype fBound.prototype = new noop(); return fBound }
new是一个关键字,我们没有办法创建一个关键字,所以只能创建一个工厂函数,要注意的是下面几点:
__prop__
function objectFactory() { var obj = new Object() var Cst = [].shift.call(arguments); obj.__prop__ = Cst.prototype // 初始化 var ret = Constructor.apply(obj, arguments); return typeof ret === 'object' ? ret : obj; }
可以有以下的使用方法
var fn = curry(function(a, b, c) { console.log([a, b, c]); }); fn("a", "b", "c") // ["a", "b", "c"] fn("a", "b")("c") // ["a", "b", "c"] fn("a")("b")("c") // ["a", "b", "c"] fn("a")("b", "c") // ["a", "b", "c"]
实现如下:
function curry(fn, args) { var length = fn.length var args = args || [] return function() { for(var i = 0;i< arguments.length;i++) { _args.push(arguments[i]) } if (_args.length < length) { return curry.call(this, fn, _args) } else { return fn.apply(this, _args) } } }
The text was updated successfully, but these errors were encountered:
No branches or pull requests
现在面试经常遇到需要手写javascript原生方法的要求。最常见的如,手写
bind
、apply
和call
方法。这些题目的目的在于考察你对于javascript的理解能力。call
实现
call
和apply
类似,有几个要点:this
上下文执行环境this
的类型不同如何处理入股偶改变
this
我们知道函数执行中,
this
的指向有三种情况,1. 使用new
构造一个实例 2. 使用构造一个属性为函数的对象,通过.
预算符调用函数 3. 出以上两种外,默认this
是全局对象,如window
.那么分析可知只有在1和2里做选择了.构造函数方法没有普遍性,所以只能通过方法2了. 我们从参数中得到上下文环境context
和要调用的函数fn
,吧fn
作为context
中的属性,调用结束后删除调这个属性就可以了.有了上下文和函数后, 之后就是如何调用了.使用
eval
函数,拼接一个函数的字符串传入进入就可以了.参数处理与传入
通过对
arguments
的浅复制, 有同学可能要问, 可以直接用Array.prototype.slice.call(arguments, 1)
得到参数的列表吗?题要求是实现自己的call
,然后在里面又用了call
是不是很怪.所以就用嘴简单的办法遍历arguments
复制到一个新的数组.得到了参数列表后,我们要在上面的eval
中拼接参数,context.fn('+args+')
里,数组会自动调用toString()
方法,转换成带有,
的字符串.传入
this
的类型不同如何处理以上已经完成了90%的工作了,最后就剩下this类型处理的分析.如果
this
是null
或者undefined
则this
为window
.如果this是Object类型的话,以上的逻辑的没有问题的,如果是基础类型的怎么办呀?基础类型是不可以添加属性的,所以我们要加一个判断,如果是对象类型的话,就用这个对象,否则就把这个对象转换成对象类型.apply
apply和call类似
bind
new操作符
new
是一个关键字,我们没有办法创建一个关键字,所以只能创建一个工厂函数,要注意的是下面几点:__prop__
手写curry
可以有以下的使用方法
实现如下:
The text was updated successfully, but these errors were encountered: