共计 3659 个字符,预计需要花费 10 分钟才能阅读完成。
1. this
除去不罕用的 with
、eval
,this
的指向大抵分为以下 4 种:
- 作为对象的办法应用
- 作为一般函数调用
- 结构器应用
Function.prototype.call
和Function.prototype.apply
调用
1.1 作为对象的办法调用
当函数作为对象的办法调用时,
this
指向该对象。如果对象的办法起了别名,this
指向全局对象,具体可见如下代码。
const person = {
userName: 'Mango',
sayName () {console.log(this);
console.log(this.userName);
}
}
person.sayName(); // Mango
// 起别名
let sayName = person.sayName;
sayName(); // undefined 此时 this 指向全局对象
1.2 一般函数调用
一般函数分为:ES5 函数 和 箭头函数(ES6)。
- ES5 中的函数,this 总是指向全局对象;
- ES6 的箭头函数,this 与定义时的上下文相干
<body>
<div id="myDiv"> 我是一个 DIV </div>
<script>
const divDom = document.querySelector('#myDiv');
divDom.addEventListener('click', function () {
// this 指向 divDom
console.log('匿名函数的 this:', this);
let callback = function () {
// this 指向 window
console.log('匿名函数中 callback this:', this);
}
let callback2 = () => {
// this 指向 divDom
console.log('匿名函数中 callback(箭头函数) this:', this);
}
callback();
callback2();})
divDom.addEventListener('click', () => { // 此时上下文在全局环境中
// this 指向 window
console.log('箭头函数的 this:', this);
let callback = function () {
// this 指向 window
console.log('箭头函数中 callback this:', this);
}
callback();})
const obj = {addEvent () {divDom.addEventListener('click', () => { // 此时上下文在 obj 对象环境中
// this 指向 obj 对象
console.log('在对象外部箭头函数的 this:', this);
let callback = function () {
// this 指向 window
console.log('对象外部匿名函数中 callback this:', this);
}
callback();})
}
}
obj.addEvent();
</script>
</body>
1.3 结构器调用
当用 new
运算符调用函数时,该函数总会返回一个对象,通常状况下,结构器中的 this
指向返回的这个对象。
const Person = function () {this.name = 'Mango';}
const person = new Person();
console.log('person name:', person.name);
1.4 call
和 apply
Function.prototype.call
和 Function.prototype.apply
能够动静扭转 this
指向,两者惟一区别是调用形式不同。
const obj1 = {
name: 'Mango',
getName () {return this.name;}
}
const obj2 = {name: 'Mango2'}
console.log(obj1.getName()); // Mango
console.log(obj1.getName.call(obj2)); // Mango2
1.5 隐没的 this
在 1.1 节中,对象的办法赋值给一个变量时(相似起别名),this
不再指向该对象,而是指向全局对象。如下:
const person = {
userName: 'Mango',
sayName () {console.log(this);
console.log(this.userName);
}
}
let sayName = person.sayName;
sayName(); // undefined
这种状况能够应用 apply
办法扭转 this
指向,最简略的是间接扭转指向,如下:
// 接下面案例
sayName.apply(person); // Mango
为了保障 sayName
的 this
不失落,能够再创立的时候就通过 apply
指明 this
, 如下:
// 应用了闭包
let sayName = (function (func, target) {return function () {return func.apply(target, arguments);
}
})(person.sayName, person);
// 也有比较简单的形式
let sayName = function () {return person.sayName();
}
能够将起别名形象为一个函数,能够指定 this
指向:
function methodAlias (func, target) {return function () {return func.apply(target, arguments);
}
}
let sayName3 = methodAlias(person.sayName, person);
sayName3();
同样的例子 还有给 document.querySelector
等函数起别名,如下所示:
// Error: 该办法外部会应用指向 document 的 this
let queryElem = document.querySelector;
queryElem('#id'); // Error
// 应用自定义的起别名办法,指明 this 指向
let queryElem = methodAlias(document.querySelector, document);
queryElem('#id');
应用 bind, 举荐
在大部分浏览器中都实现了 Function.prototype.bind
办法,能够如下应用:
let sayName4 = person.sayName.bind(person);
sayName4(); // Mango
let queryElem = document.querySelector.bind(document);
queryElem('#id');
bind
办法的实现原理如下:
Function.prototype.bind = function (context) {
let self = this;
return function () {self.apply(context, arguments);
}
}
2. call 与 apply
2.1 简述
call
与 apply
只是在模式是不同,如下所示:
const func = function (a, b, c) {console.log([a, b, c]);
}
// apply 的第二个参数是数组或类数组
func.apply(null, [1, 2, 3]);
// call 会将函数的参数顺次往后排,call 是 apply 的一个语法糖
func.call(null, 1, 2, 3);
}
当 call
和 apply
的第一个参数为 null
时,函数(一般函数与对象办法) 体内的 this
会指向默认的宿主对象,在浏览器中是 window
。
window.age = 200;
const obj = {
age: 28,
func2: function () {console.log('func2:', this.age);
}
}
const func1 = function () {console.log('func1:', this.age);
}
func1.apply(null); // 200 this 指向 window
func1.apply(obj); // 28 this 指向 obj
obj.func2.apply(null); // 200 this 指向 window
obj.func2.apply(obj); // 28 this 指向 obj
2.2 call 和 apply 的其余用处
借用其余对象的办法
最典型的就是类数组借用 Array.prototype
中的办法。
比方函数的参数列表 arguments
是一个类数组对象,尽管它也有“下标”,但它不是真正的数组,所以不能像数组一样应用数组的办法。
这种状况就能够应用 Array.prototype
中的办法。
个别状况下,对象有“下标”和 length
属性即可应用数组的原型办法。
罕用办法如下所示:
// 将类数组转为数组
Array.prototype.slice.apply(arguments);
// 往类数组中增加元素
Array.prototype.push.apply(arguments, ['one']);
//...