this 的 绑定例外的意外收获 和使用(
故¸意忽略的this、方法函数的间接引用
)this 的 三种绑定 方法的使用 (
call()、apply()、bind()
)-
箭头函数的 this 指向和使用
this 的三篇文章按照顺序复习 this基本概念 -> this绑定规则 -> this 应用实例
this 的绑定例外
1. apply call bind 绑定例外的大作用
2. 使用更加安全的 对象( 很纯洁的空对象 )
-
被忽略的 this, 显示绑定和硬绑定看起来很牛的样子,真的是吗?其实不然
若把 null、undefined 作为 绑定对象传入 call 或者 bind 中,这些值在调用过程中就会被忽略,实际应用的是默认的绑定规则** 一下子倒退可一大步呀 **var n = 123; var obj = { n: 456 }; function a() { console.log(this.n); } a.call() // 123 a.call(null) // 123 a.call(undefined) // 123 a.call(window) // 123 a.call(obj) // 456
-
示例一:使用 bind 来传入默认的函数参数
参数的柯里化
function foo(a1, a2) { this.val = a1 + a2; } var bar = foo.bind(null, 100) var baz = new bar('人民币'); console.log(baz.val); // 100人民币
分析
** bind 可以进行函数的默认传参使用,可以配合 new 构造函数调用(示例),或者普通函数调用进行参数
柯里化
** -
示例二: apply 的 花式传参( ** 利用 apply 可以将数组展开的特性 ** )
function foo(a, b){ console.log('a:' + a + ' b: ' + b); } foo.apply(null, [1000, '美元']); // 1000 美元 var bar = foo.bind(null, '100'); bar('欧元'); // 100 欧元
总是使用 null 来忽略 this 绑定可能会产生一些副作用,若某个函数内部已经使用了 this, 忽略 this 的默认绑定规则会把 this 绑定修改到全局作用域( 严格模式报错 )。这样会有很严重的后果??怎么办??
-
我们找一个很干净的对象来作为 this 的默认绑定对象,这样就会避免上述问题。
创建一个无比纯净的空对象 Object.create(null)
** 这个对象是一个空对象,没有任何的 prototype 原型对象,没有数据,就是什么都没有, 比 {} 还空的一个对象 **
我们常常 使用 这个字符来表示它 ** ø ** var ø = Object.create(null);
** 使用这个对象作为 this 的被忽略绑定的对象和 null 或者 undefined
得到的结果基本一致
能得到 更加安全的 this 指向( 而不是指向全局的作用域 )
-
bind 方法的一个小问题
bind 方法运行一次就会产生一个新的函数,所以在事件监听等可多次重复触发的事件中需要特别注意element.addEventListener('click', fun.bind(o)); // 可能有产生多个函数 // 正确的姿势 var listener = o.m.bind(o); element.addEventListener('click', listener); // ... element.removeEventListener('click', listener);
apply call bind 使用实例
-
apply 应用找出数组的最大元素
var a = [1, 23, 123, 45, 77]; Math.max.apply(null, a) // 123;
这里的 apply 只是为了展开上面的数组a
-
apply 应用设置数组元素
var arr = new Array(10); // undefined * 10
** 数组元素为空或者 undefined 在遍历时会得到不同的结果 ( 后阶段会有详细讲解 )**
var arr = ['1', , , , 'bar'];
Array.apply(null, arr); // ["1", undefined, undefined, undefined, "bar"]
这里使用 apply 展开了 arr 数组
-
转换类数组对象。
类数组对象的概念在 es6 中有详细讲解 ( 具体表现为 有 length 属性的对象,eg: 参数 argument )
var a = { 0: 'aa', 1: "1", length: 4 } var arr = Array.prototype.slice.apply(a); console.log(arr);
这里注意类数组对象的写法。
-
解决回调函数 作为参数传递 中的 this 指向问题
若作为参数的回调函数中包含this 则 该 this 的指向很可能出错
版本一: 不知所踪的 this
var person = {
name: '张三',
time: [1, 3, 6],
print: function() {
this.time.forEach(function(element) {
console.log('person name: ' + this.name);
});
}
}function doSometing(callback){ callback(); } doSometing(person.print); 函数执行报错。因为 this.time 不知道 指向什么。
版本二: bind 绑定 回调函数中的 this 指向 一个对象
var person = {
name: '张三',
time: [1, 3, 6],
print: function() {
this.time.forEach(function(element) {
console.log('person name: ' + this.name);
});
}
}
function doSometing(callback){
callback();
}
// person name: undefined
// person name: undefined
// person name: undefined
doSometing(person.print.bind(person)); // 注意: 这里比上面多了 一个 bind(person);
这个 版本的回调函数绑定 了 this, 所以 print 函数的执行 不会报错,但是 forEach 循环中的this 指向仍不明确
版本三: forEach 循环中也是可以绑定 this 的嘛
var person = {
name: '张三',
time: [1, 3, 6],
print: function() {
this.time.forEach(function(element) {
console.log('person name: ' + this.name);
}.bind(this)); // 注意 这里绑定了 this
}
}
function doSometing(callback){
callback();
}
doSometing(person.print.bind(person)); // 注意: 这里绑定了 this
// person name: 张三
// person name: 张三
// person name: 张三
第三个输出没有任何问题
-
bind 的扩展版本:对于 forEach map 等数组的操作方法 都可以传递第二个参数( 一个对象 ) 用于绑定一个 循环内部的 this 指向。其内部也是使用 bind 方法实现
var person = { name: "张三", age: '24' } function doSometing(){ console.log(this.name + this.age); } [1, 2, 3, 4].forEach(doSometing, person); // 张三 24 * 4
this 的词法作用域(箭头函数中的 this 的指向)
箭头函数中的this 根据外层( 函数或者全局 ) 作用域来决定 this
.