前言
最近在在搞react的时候有用到bind()的时候,因为他的用法其实我还一直不是特别的清楚,所以今天我把bind()他的用法和我遇到的结合起来这样来写一个博客,这样应该可以加深自己的印象同时可以来跟好的来解析bind的使用方法
1.那么我们先来介绍一下bind()
function.prototype.bind()
bind()方法主要就是将函数绑定到某个对象,bind()会创建一个函数,函数体内的this对象的值会被绑定到传入bind()中的第一个参数的值,例如:f.bind(obj),实际上可以理解为obj.f(),这时f函数体内的this自然指向的是obj;
这个可能大家都很明白了,那么我们可以来看下下面的这个例子
var a = {
b: function() {
var func = function() {
console.log(this.c);
}
func();
},
c: 'hello'
}
a.b();
console.log(a.c);
那么答案是什么呢?undefined和hello,大家可以打印看下,这个就是因为fun()这个函数执行的时候他的函数上下文为window,而a.b()的这个函数的执行的时候函数上下文this为a对象是什么意思呢?
var a = {
b: function() {
console.log(this.c); //hello
var func = function() {
console.log(this.c); //undefined
}
func();
},
c: 'hello'
}
a.b();
看了上面的例子和console.log()的结果大家应该是知道了这个函数上下文大概是这么一回事了把?什么还是不会好把看这里,这里有关于this指向的问题的解析那么问题来了当我们希望func()他的输出的值就是为hrllo怎么办
方法一:改变this的值
var a = {
b: function() {
var _this = this; // 通过赋值的方式将this赋值给that
var func = function() {
console.log(_this.c);
}
func();
},
c: 'hello'
}
a.b(); // hello
console.log(a.c); // hello
方法二:绑定this的值发生改变
// 使用bind方法一
var a = {
b: function() {
var func = function() {
console.log(this.c);
}.bind(this);
func();
},
c: 'hello'
}
a.b(); // hello
console.log(a.c); // hello
// 使用bind方法二
var a = {
b: function() {
var func = function() {
console.log(this.c);
}
func.bind(this)();
},
c: 'hello'
}
a.b(); // hello
console.log(a.c); // hello
这里我们以a.b()的形式去执行a对象中的b这个函数,是经过对象a的所以当我们来执行a对象中b函数的时候找this就会先找到a对象所以在a对象中的b这个函数中的this为a对象,所以这个时候bind,绑定的this也就是为a对象了
c = 10;
var a = {
b: function () {
console.log(this);
var func = function () {
console.log(this.c);
}.bind(this);
func();
},
c: 'hello'
}
var d = a.b;
d();
这里我们以d()的形式去执行a对象中的b这个函数吗,因为d()的执行的时候由于没人应用this默认为window,所以在a对象中的b这个函数中的this为window,所以不这个时候bind,绑定的this也就是为window了
bind()的原生实现分步解析
1:通过call,吧arguments生成一个真正的数组
array.prototype.slice.call(arguments)是用来将参数由类数组转换为真正的数组;
[].slice.call(arguments)是用来将参数由类数组转换为真正的数组;
上面两中方法的结果一样
解析:其实就是arguments他是一个类数组(对象)他没slice的方法 ,通过上面的array.prototype.slice写法可以让他添加slice的方法,为什么可以呢?
slice()没参数默认从key0到最后一项,浅拷贝,不影响原数组
因为其实arguments也是一个对象,就相当与是在arguments中执行了slice()函数,那么不要参数的情况下就是根据key下标会返回一个完整的数组从而达到了生成了一个新的数组的效果,arguments对象中是有length属性和0.1.2.这样的属性的而slice就是会根据这些0.1.2的这些key(也叫属性值的东西)放回成为一个数组
原生bing实现
function.prototype.mybind = function () {
if (typeof this !== 'function') throw 'caller must be a function'
let self = this // 这里是关键 用来和new出来的比较原型来判断是否为new出来的 当前函数对象
let context = arguments[0]
let args = array.prototype.slice.call(arguments, 1) // 旧:参数
let fn = function () {
let fnargs = array.prototype.slice.call(arguments) // 新:参数
// bind 函数的参数 延迟函数的参数
// 用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上
self.apply(this instanceof self ? this : context, args.concat(fnargs))
}
fn.prototype = object.create(self.prototype) // 维护原型
return fn
}
关键点
1.bing是影响不到new构造函数过程中this为新构造出来的那个对象的定义
当一个绑定函数是用来构建一个值的,原来提供的
this
就会被忽略。 ---mdn代码:this instanceof self
this:对mybing出来的函数所用者的指向
self:使用mybing()的那个函数
instanceof:用来检查构造函数的prototype属性是否出现在某一个实例对象的原型链上
代码: fn.prototype = object.create(self.prototype) // 维护原型,用来满足instanceof的条件(下面分析)
2.由于返回的是新定义出来的函数,所以原型要记得绑定
3.object.create() 专门用来绑定__proto__的
分析
这里帮忙分析下子在用new流程的时候为什么要---(this instanceof self)
var yaxispoint = point.mybind(null, 0/*x*/);
var axispoint = new yaxispoint(5);
我们以上面代码为例子,所以可以得出
1. self:point 函数
我们使用关键词new的时候在执行yaxispoint函数,那么yaxispoint 函数中的this就是我们new出来的实例了
2..this = 新出来的实例对象
由于this等于新new出来的实例其实this instanceof self不应该为true的,因为新创建的函数和引用mybind的函数(self函数)完全无瓜葛
但是,fn.prototype = object.create(self.prototype),导致fn构造函数new出来的实例指向的prototype和引用mybind的函数(self函数)的prototype是一样的,所以this instanceof self === true
意外的痛苦
apply和call实现
function.prototype.newapplyorcall = function () {
// 预防
if (typeof this !== 'function') throw '请使用函数调用'
let self = arguments[0] || window // 虽然是必填项但是在非严格模式,null undefined 会默认转为window
let args = [...arguments].flat().slice(1)
let fn = symbol("_newapplyorcallfn");
self[fn] = this // 这里添加属性还是有点问题,并不可以完全删除
// reflect.deleteproperty(self, fn) // 删除属性
const res = self[fn](...args)
// delete self[fn]
reflect.deleteproperty(self, fn) // 删除属性
return res
}