bind是什么
bind()方法创建一个新的函数, 当被调用时,将其this关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列。
var result = fun.bind(thisArg[, arg1[, arg2[, ...]]]) result(newArg1, newArg2...)
没看懂没事接着往下看。
bind到底做了什么
从上面的介绍中可以看出三点。首先调用bind方法会返回一个新的函数(这个新的函数的函数体应该和fun是一样的)。同时bind中传递两个参数,第一个是this指向,即传入了什么this就等于什么。如下代码所示:
this.value = 2
var foo = {
value: 1
}
var bar = function() {
console.log(this.value)
}
var result = bar.bind(foo)
bar() // 2
result() // 1,即this === foo
第二个参数为一个序列,你可以传递任意数量的参数到其中。并且会预置到新函数参数之前。
this.value = 2
var foo = {
value: 1
};
var bar = function(name, age, school) {
console.log(name) // 'An'
console.log(age) // 22
console.log(school) // '家里蹲大学'
}
var result = bar.bind(foo, 'An') //预置了部分参数'An'
result(22, '家里蹲大学') //这个参数会和预置的参数合并到一起放入bar中
我们可以看出在最后调用 result(22, '家里蹲大学') 的时候,其内部已经包含了在调用bind的时候传入的 'An'。
一句话总结:调用bind,就会返回一个新的函数。这个函数里面的this就指向bind的第一个参数,同时this后面的参数会提前传给这个新的函数。调用该新的函数时,再传递的参数会放到预置的参数后一起传递进新函数。
自己实现一个bind
实现一个bind需要实现以下两个功能
返回一个函数,绑定this,传递预置参数
bind返回的函数可以作为构造函数使用。故作为构造函数时应使得this失效,但是传入的参数依然有效
1、返回一个函数,绑定this,传递预置参数
this.value = 2
var foo = {
value: 1
};
var bar = function(name, age, school) {
console.log(name) // 'An'
console.log(age) // 22
console.log(school) // '家里蹲大学'
console.log(this.value) // 1
}
Function.prototype.bind = function(newThis) {
var aArgs = Array.prototype.slice.call(arguments, 1) //拿到除了newThis之外的预置参数序列
var that = this
return function() {
return that.apply(newThis, aArgs.concat(Array.prototype.slice.call(arguments)))
//绑定this同时将调用时传递的序列和预置序列进行合并
}
}
var result = bar.bind(foo, 'An')
result(22, '家里蹲大学')
这里面有一个细节就是Array.prototype.slice.call(arguments, 1) 这句话,我们知道arguments这个变量可以拿到函数调用时传递的参数,但不是一个数组,但是其具有一个length属性。为什么如此调用就可以将其变为纯数组了呢。那么我们就需要回到V8的源码来进行分析。#这个版本的源码为早期版本,内容相对少一些。
function ArraySlice(start, end) {
var len = ToUint32(this.length);
//需要传递this指向对象,那么call(arguments),
//便可将this绑定到arguments,拿到其length属性。
var start_i = TO_INTEGER(start);
var end_i = len;
if (end !== void 0) end_i = TO_INTEGER(end);
if (start_i < 0) {
start_i += len;
if (start_i < 0) start_i = 0;
} else {
if (start_i > len) start_i = len;
}
if (end_i < 0) {
end_i += len;
if (end_i < 0) end_i = 0;
} else {
if (end_i > len) end_i = len;
}
var result = [];
if (end_i < start_i)
return result;
if (IS_ARRAY(this))
SmartSlice(this, start_i, end_i - start_i, len, result);
else
SimpleSlice(this, start_i, end_i - start_i, len, result);
result.length = end_i - start_i;
return result;
};
从源码中可以看到通过call将arguments下的length属性赋给slice后,便可通过 start_i & end_i来获得最后的数组,所以不需要传递进slice时就是一个纯数组最后也可以得到一个数组变量。
2、bind返回的函数可以作为构造函数使用
被用作构造函数时,this应指向new出来的实例,同时有prototype属性,其指向实例的原型。
this.value = 2
var foo = {
value: 1
};
var bar = function(name, age, school) {
...
console.log('this.value', this.value)
}
Function.prototype.bind = function(newThis) {
var aArgs = Array.prototype.slice.call(arguments, 1)
var that = this //that始终指向bar
var NoFunc = function() {}
var resultFunc = function() {
return that.apply(this instanceof that "htmlcode">
var NoFunc = function() {}
...
NoFunc.prototype = that.prototype //that指向bar
resultFunc.prototype = new NoFunc()
return resultFunc
通过上面代码可以看出,that始终指向bar。同时返回的函数已经继承了that.prototype即bar.prototype。为什么不直接让返回的函数的prototype属性resultFunc.prototype 等于为bar(that).prototype呢,这是因为任何new出来的实例都可以访问原型链。如果直接赋值那么new出来的对象可以直接修改bar函数的原型链,这也就是是原型链污染。所以我们采用继承的方式(将构造函数的原型链赋值为父级构造函数的实例),让new出来的对象的原型链与bar脱离关系。
2.判断当前被调用时,this是用于普通的bind还是用于构造函数从而更改this指向。
如何判断当前this指向了哪里呢,通过第一点我们已经知道,通过bind方法返回的新函数已经有了原型链,剩下需要我们做的就是改变this的指向就可以模拟完成了。通过什么来判断当前被调用是以何种姿势呢。答案是instanceof 。
instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。
// 定义构造函数
function C(){}
function D(){}
var o = new C();
// true,因为 Object.getPrototypeOf(o) === C.prototype
o instanceof C;
// false,因为 D.prototype不在o的原型链上
o instanceof D;
从上面可以看出,instanceof可以判断出一个对象是否是由这个函数new出来的,如果是new出来的,那么这个对象的原型链应为该函数的prototype.
所以我们来看这段关键的返回的函数结构:
var resultFunc = function() {
return that.apply(this instanceof that "htmlcode">
if (!Function.prototype.bind) {
Function.prototype.bind = function(oThis) {
if (typeof this !== 'function') {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function() {},
fBound = function() {
return fToBind.apply(this instanceof fNOP
? this
: oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
if (this.prototype) {
// Function.prototype doesn't have a prototype property
fNOP.prototype = this.prototype;
}
fBound.prototype = new fNOP();
return fBound;
};
}
可以看到,其首先做了当前是否支持bind的判定,不支持再实行兼容。同时判断调用这个方法的对象是否是个函数,如果不是则报错。
同时这个模拟的方法也有一些缺陷,可关注MDN上的Polyfill部分
小结
模拟bind实现最大的一个缺陷是,模拟出来的函数中会一直存在prototype属性,但是原生的bind作为构造函数是没有prototype的,这点打印一下即可知。不过这样子new出来的实例没有原型链,那么它的意义是什么呢。
大家在学习的时候如果还有任何问题,可以在下面的留言区讨论,感谢你的支持。
Javascript,bind
免责声明:本站文章均来自网站采集或用户投稿,网站不提供任何软件下载或自行开发的软件! 如有用户或公司发现本站内容信息存在侵权行为,请邮件告知! 858582#qq.com
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新日志
- 小骆驼-《草原狼2(蓝光CD)》[原抓WAV+CUE]
- 群星《欢迎来到我身边 电影原声专辑》[320K/MP3][105.02MB]
- 群星《欢迎来到我身边 电影原声专辑》[FLAC/分轨][480.9MB]
- 雷婷《梦里蓝天HQⅡ》 2023头版限量编号低速原抓[WAV+CUE][463M]
- 群星《2024好听新歌42》AI调整音效【WAV分轨】
- 王思雨-《思念陪着鸿雁飞》WAV
- 王思雨《喜马拉雅HQ》头版限量编号[WAV+CUE]
- 李健《无时无刻》[WAV+CUE][590M]
- 陈奕迅《酝酿》[WAV分轨][502M]
- 卓依婷《化蝶》2CD[WAV+CUE][1.1G]
- 群星《吉他王(黑胶CD)》[WAV+CUE]
- 齐秦《穿乐(穿越)》[WAV+CUE]
- 发烧珍品《数位CD音响测试-动向效果(九)》【WAV+CUE】
- 邝美云《邝美云精装歌集》[DSF][1.6G]
- 吕方《爱一回伤一回》[WAV+CUE][454M]