Open sisterAn opened 4 years ago
用apply实现bind??
面试的时候这样写会有什么问题吗?🙃
Function.prototype.myBind = function(context, ...args1) {
if(typeof this !== 'function') {
throw Error('error')
}
context = context ? Object(context) : Window
const currentFunc = this
return function(...args2) {
if(new.target) {
return new currentFunc(...[...args1, ...args2])
}
return currentFunc.apply(context, [...args1, ...args2])
}
}
我也是直接返回的new self
Function.prototype._bind = function(ctx) {
const beforeArgs = [...arguments].slice(1)
const self = this
const fn = function() {
const fullArgs = beforeArgs.concat([...arguments])
// this是fn的实例,说明是new出来的
if (this instanceof fn) {
return new self(...fullArgs) // 这里有什么问题吗??
}
return self.apply(ctx, [...fullArgs])
}
return fn
}
let value = 2;
let foo = {
value: 1
};
function bar(name, age) {
this.habit = 'shopping';
console.log(this.value);
console.log(name);
console.log(age);
}
bar.prototype.friend = 'kevin';
let bindFoo = bar.bind(foo, 'Jack');
let obj = new bindFoo(20);
// undefined
// Jack
// 20
obj.habit;
// shopping
obj.friend;
// kevin
这个我运行出来不是 // undefined // Jack // 20 而是 // 1 // Jack // 20 为啥啊?
let value = 2; let foo = { value: 1 }; function bar(name, age) { this.habit = 'shopping'; console.log(this.value); console.log(name); console.log(age); } bar.prototype.friend = 'kevin'; let bindFoo = bar.bind(foo, 'Jack'); let obj = new bindFoo(20); // undefined // Jack // 20 obj.habit; // shopping obj.friend; // kevin
这个我运行出来不是 // undefined // Jack // 20 而是 // 1 // Jack // 20 为啥啊?
你这个也没有考虑bind生成的函数在使用new关键字调用的情况呀 我们在使用new关键字调用方法bindFoo的时候,传入的context是不起作用的,我们可以直接忽视 我们可以通过这个方法判断是否是使用new关键字调用的“myBind方法返回匿名函数当中的this是指向这个匿名函数的”
Function.prototype.myBind = function (obj) {
if (typeof this !== 'function') {
throw new TypeError('bind is only work for Function')
}
let first = Array.prototype.slice.call(arguments, 1);//因为第一个参数是obj
let fn_ = function () { };
let fn = this;//调用bind的函数
fn_.prototype = fn.prototype;
const bindFn = function () {
let second = Array.prototype.slice.call(arguments);
fn.apply(this.constructor === fn ? this : obj, first.concat(second));
console.log(this);
}
bindFn.prototype = new fn_();
return bindFn;
}
我不理解,修改后的版本,this.value不还是输出undefined吗...
我不理解,修改后的版本,this.value不还是输出undefined吗...
返回undefined 才是对的,修改之前的版本返回的是1,使用new 调用 this 应该是指向新创建的对象 undefined 才是对的~
不理解,this为何是bar函数
这是来自QQ邮箱的假期自动回复邮件。 您好,我最近正在休假中,无法亲自回复您的邮件。我将在假期结束后,尽快给您回复。
return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs)); 这句写错了啊,正确的应该是: return self.apply(this instanceof fBound ? this : context, args.concat(bindArgs));
if(new.target)是什么意思哇?
@yysfwls 判断函数是否通过 new 调用。
这是来自QQ邮箱的假期自动回复邮件。 您好,我最近正在休假中,无法亲自回复您的邮件。我将在假期结束后,尽快给您回复。
面试的时候这样写会有什么问题吗?🙃
Function.prototype.myBind = function(context, ...args1) { if(typeof this !== 'function') { throw Error('error') } context = context ? Object(context) : Window const currentFunc = this return function(...args2) { if(new.target) { return new currentFunc(...[...args1, ...args2]) } return currentFunc.apply(context, [...args1, ...args2]) } }
ES6的new.target语法,取巧了
Function.prototype._bind = function(t){
let self = this;
let a =[].slice.call(arguments,1)
return function(){
self.apply(t,a.concat(arguments))
}
}
Function.prototype.myBind = function (ctx) {
// 调用 bind 的不是函数,需要抛出异常
if (typeof this !== "function") {
throw new Error("ctx must be function");
}
const argv1 = Array.prototype.slice.call(arguments, 1)
const self = this
return function _bind() {
const argv2 = Array.prototype.slice.call(arguments)
const argv = argv1.concat(argv2)
if (Object.getPrototypeOf(this) === _bind.prototype) {
// return new self(...argv)
var obj = Object.assign({}, ctx)
Object.setPrototypeOf(obj, self.prototype)
self.apply(obj, argv)
return obj
} else {
return self.apply(ctx, argv)
}
}
}
// 测试1
function test(a, b, c, d) {
console.log(a, b, c, d, 'test')
return 'x'
}
const module = {
x: 42,
getX: function () {
return this.x
},
}
const bindTestFn = test.myBind(module, 1, 2)
bindTestFn(3, 4)
// 测试2
let value = 2;
let foo = {
value: 1
};
function bar(name, age) {
this.habit = 'shopping';
console.log(this.value);
console.log(name);
console.log(age);
}
bar.prototype.friend = 'kevin';
const bindFoo = bar.myBind(foo, 'Jack')
let obj = new bindFoo(20);
console.log(obj, obj.habit, obj.friend)
这是来自QQ邮箱的假期自动回复邮件。 您好,我最近正在休假中,无法亲自回复您的邮件。我将在假期结束后,尽快给您回复。
bind()
bind
方法与call / apply
最大的不同就是前者返回一个绑定上下文的函数,而后两者是直接执行了函数。来个例子说明下:
通过上述代码可以看出
bind
有如下特性:this
模拟实现:
但还有一个问题,
bind
有以下一个特性:来个例子说明下:
上面例子中,运行结果
this.value
输出为undefined
,这不是全局value
也不是foo
对象中的value
,这说明bind
的this
对象失效了,new
的实现中生成一个新的对象,这个时候的this
指向的是obj
。这个可以通过修改返回函数的原型来实现,代码如下:
注释1 :
this
指向实例,此时this instanceof fBound
结果为true
,可以让实例获得来自绑定函数的值,即上例中实例会具有habit
属性。this
指向window
,此时结果为false
,将绑定函数的this
指向context
注释2 :
prototype
为绑定函数的prototype
,实例就可以继承绑定函数的原型中的值,即上例中obj
可以获取到bar
原型上的friend
fNOP
作为中介,把fBound.prototype
赋值为空对象的实例(原型式继承),这是因为直接fBound.prototype = this.prototype
有一个缺点,修改fBound.prototype
的时候,也会直接修改this.prototype
;其实也可以直接使用ES5的Object.create()
方法生成一个新对象,但bind
和Object.create()
都是ES5方法,部分IE浏览器(IE < 9)并不支注意:
bind()
函数在 ES5 才被加入,所以并不是所有浏览器都支持,IE8
及以下的版本中不被支持,如果需要兼容可以使用 Polyfill 来实现详情可前往 深度解析bind原理、使用场景及模拟实现 查看
补充:柯里化
在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。这个技术由 Christopher Strachey 以逻辑学家 Haskell Curry 命名的,尽管它是 Moses Schnfinkel 和 Gottlob Frege 发明的。
这里定义了一个
add
函数,它接受一个参数并返回一个新的函数。调用add
之后,返回的函数就通过闭包的方式记住了add
的第一个参数。所以说bind
本身也是闭包的一种使用场景。柯里化是将
f(a,b,c)
可以被以f(a)(b)(c)
的形式被调用的转化。JavaScript 实现版本通常保留函数被正常调用和在参数数量不够的情况下返回偏函数这两个特性。