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()); // Mangoconsole.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 的 thislet 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(); // Mangolet 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指向windowfunc1.apply(obj); // 28 this指向 objobj.func2.apply(null); // 200 this 指向 windowobj.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']);//...