this关键字可以用在构造函数之中,表示实例对象。除此之外还可以用在别的场合。但是不管是什么场合,this都有一个共同点:它总是返回一份对象
函数在浏览器全局环境下被简单调用,在非严格模式下 this 指向 window,在通过 use strict 指明严格模式的情况下指向 undefined。
例1:
function f1() {console.log(this);
}function f2() {'use strict'console.log(this);
}f1() // window or global
f2() // undefined
例2:
const foo = {bar : 10,fn : function(){console.log(this); console.log(this.bar); }
}
var fn1 = foo.fn;
fn1();// window or global
// undefined
虽然fn函数在foo对象中作为该对象的一个方法,但是赋值给fn1之后,fn1仍然是在window的全局环境下执行的。因此this指向的还是window
例3:
const foo = {bar : 10,fn : function(){console.log(this); console.log(this.bar); }
}
foo.fn(); // { bar: 10, fn: [Function: fn] }
// 10
这里,this指向调用它的对象,在foo.fn() 语句中,this指向的是foo对象
一般通过上下文对象调用函数时,函数体内的 this 会被绑定到该对象上。
例4:
const student = {name: 'zhangsan',fn: function () {return this;}
}
console.log(student.fn() === student); // true
this 指向当前的对象student
在嵌套关系中,this指向最后调用它的对象
例5:
const student = {name: 'zhangsan',son: {name: 'zhangxiaosan',fn: function () {return this.name}}
}
console.log(student.son.fn()); // zhangxiaosan
高阶-例6:
const o1 = {text: 'o1',fn: function () {return this.text;}
}const o2 = {text: 'o2',fn: function () {return o1.fn();}
}const o3 = {text: 'o3',fn: function () {var fn = o1.fn;return fn();}
}console.log(o1.fn());
console.log(o2.fn());
console.log(o3.fn());// o1// o1
// undefined
o1.fn() this指向调用它的对象,打印为o1;
o2.fn() this 指向为o1,打印为o1;
o3.fn() 这里是将o1.fn 赋值给fn,并return,所以在调用的时候,相当于全局调用function () { return this.text; },并不是以对象的形式调用,this指向window,所以打印为undefined
问题:在一个div节点的时间函数内部,有一个局部的callback方法,我们希望callback方法内部的this指向div节点
我是一个divwindow.id = 'window';
document.getElementById('div1').onclick = function(){console.log('this1',this.id); const callback = function(){console.log(this.id); }callback();
}

由于callback作为普通函数被调用,所以this指向为window
解决: 通过变量保存的方式
window.id = 'window';
document.getElementById('div1').onclick = function(){console.log('this1',this.id); // div1const that = this; // 保存当前 this 的指向const callback = function(){console.log(that.id); }callback();
}

箭头函数中的this 指向始终是指向的外层作用域(箭头函数没有自己的this, 它的this是继承而来; 默认指向在定义它时所处的对象(宿主对象),此处指父级作用域)
var x = 20;
const obj = {x: 10,test: () => {console.log(this); // {}console.log(this.x); // undefined}
}
obj.test();

箭头函数的 this 指向与普通函数不一样,它的 this 指向始终是指向的外层作用域。所以这里的 this 实际上是指向的全局对象。
var name = "JavaScript";
const obj = {name: "PHP",test: function () {const i = function () {console.log(this.name);// i 是以函数的形式被调用的,所以 this 指向全局// 在浏览器环境中打印出 JavaScript,node 里面为 undeifned}i();}
}
obj.test(); // JavaScript// 改为箭头函数:var name = "JavaScript";
const obj = {name : "PHP",test : function(){const i = ()=>{console.log(this.name);// 由于 i 为一个箭头函数,所以 this 是指向外层的// 所以 this.name 将会打印出 PHP}i();}
}
obj.test();// PHP
另外箭头函数 不能作为构造函数
const Test = (name, age) => {this.name = name;this.age = age;
};
const test = new Test("xiejie", 18);
// TypeError: Test is not a constructor
call方法可以指定this指向(即函数执行时所在的作用域),然后在指定的作用域中执行函数。
// 函数.call(对象)
fun.call(thisArg, arg1, arg2, ...)
thisArg:在 fun函数运行时指定的 this值 。如果参数为空或 null、undefind,则默认传参全局对象,同时值为原始值(数字,字符串,布尔值)的 this会指向该原始值的自动包装对象。
例1:改变this指向
var obj = {};
var f = function(){return this;
};
console.log(f() === window);
console.log(f.call(obj) === obj)
执行f() 时,因为在全局 环境下执行,所以this指向window,通过call 传入第一个参数,call前面的函数执行时,this指向为第一个参数对象
例2:this指向传入undefined 、null
var n = 123;
var obj = { n: 456 };function a() {console.log(this.n);
}a.call() // 传入空,指向全局 ,123
a.call(null) //传入null 指向全局, 123
a.call(undefined) //传入undefined, 123
a.call(window) // 123
a.call(obj) //传入obj,this指向obj 456
例3: 传入Number类型,this指向包装对象
var f = function () {return this;
};f.call(5); // Number {[[PrimitiveValue]]: 5}

第一个参数是 this 指向的对象,之后的是函数回调所需的参数
例4:
function add(a, b) {return a + b;
}add.call(this, 1, 2) // 3
hasOwnProperty 该方法是查看一个对象是否有某一个属性或者方法
这个属性或者方法必须是自身就有的,而不是继承而来
var obj = {};
obj.hasOwnProperty('toString') // false// 覆盖掉继承的 hasOwnProperty 方法
obj.hasOwnProperty = function () {return true;
};
obj.hasOwnProperty('toString') // trueObject.prototype.hasOwnProperty.call(obj, 'toString') // false
上面代码中 hasOwnProperty 是 obj 继承来的方法,用来判断对象是否包含自身特点(非继承)属性,但是 hasOwnProperty 并不是保留字,如果被对象覆盖,会造成结果错误。
call 方法可以解决这个问题,它将 hasOwnProperty 方法的原始定义放到 obj 对象上执行,这样无论 obj 上有没有同名方法,都不会影响结果。
func.apply(thisValue, [arg1, arg2, ...])
相同点:
不同点:
function f(x, y){console.log(x + y);
}f.call(null, 1, 1) // 2
f.apply(null, [1, 1]) // 2
1. 输出数组的最大值
var a = [24,30,2,33,1]
Math.max.apply(null,a) //33
2. 将数组的空元素转化成undefined
意义:数组的 forEach 方法会跳过空元素,但是不会跳过 undefined。undefined可以通过forEach循环出来
var a = ['a', , 'b'];function print(i) {console.log(i);
}a.forEach(print)
// a
// bArray.apply(null, a).forEach(print)
// a
// undefined
// b
3. 配合数组的slice方法,实现类数组转化为真正的数组
Array.prototype.slice.apply({0: 1, length: 1}) // [1]
Array.prototype.slice.apply({0: 1}) // []
Array.prototype.slice.apply({0: 1, length: 2}) // [1, undefined]
Array.prototype.slice.apply({length: 1}) // [undefined]
将类似数组的对象转化为数组
f.bind(obj)
bind用于将函数体内的this绑定到某个对象,然后返回一个新函数:
例:
问题:
var d = new Date();
d.getTime() // 1481869925657var print = d.getTime;
print() // Uncaught TypeError: this is not a Date object.
执行print时,将getTime赋值给print,相当于全局调用,此时this指向window
使用bind解决:
var print = d.getTime.bind(d);
print() // 1481869925657
通过bind,返回一个新函数,这个新函数this被绑定到了d上
bind接收的参数就是所要绑定的对象
常规绑定:
var counter = {count: 0,inc: function () {this.count++;}
};var func = counter.inc.bind(counter);
func();
counter.count // 1
bind中传入对象为counter,所以bind前函数执行时this指向counter
绑定到其他对象:
var counter = {count: 0,inc: function () {this.count++;}
};var obj = {count: 100
};
var func = counter.inc.bind(obj);
func();
obj.count // 101
传入的对象为obj,所以执行bind前的函数时,函数指向obj
bind 接受多个参数时,除了第一个参数,其他参数会绑定到原函数的参数
var add = function (x, y) {return x * this.m + y * this.n;
}var obj = {m: 2,n: 2
};var newAdd = add.bind(obj, 5);
newAdd(5) // 20
bind的第一个参数为obj,所以将bind前函数的this指向为obj,后面的参数会作为调用add方法时的参数传入
若 bind 方法的第一个参数是 null 或 undefined,等于将 this 绑定到全局对象,函数运行时 this 指向顶层对象(浏览器为 window)。
function add(x, y) {return x + y;
}var plus5 = add.bind(null, 5);
plus5(10) // 15
函数add内没有使用this,bind方法主要目的是绑定x,y参数,每次运行plus5时,只需要传入另一个参数y就行了。
事件监听时:
由于每次运行时,就会返回一个新函数。所以上面的代码click事件绑定bind方法会生成一个匿名函数。导致无法取消绑定
element.addEventListener('click', o.m.bind(o));//取消绑定时
element.removeEventListener('click', o.m.bind(o));
解决:
var listener = o.m.bind(o);
element.addEventListener('click', listener);
// ...
element.removeEventListener('click', listener);
var counter = {count: 0,inc: function () {'use strict';this.count++;}
};function callIt(callback) {callback();
}
// 写法1: callIt(counter.inc())
callIt(counter.inc.bind(counter));
counter.count // 1
使用如上写法1 的方式传入函数,调用时,this会指向window,相当于全局调用。解决方式就是通过bind绑定this
追问:某些数组方法接收的函数中的this指向
var obj = {name: '张三',times: [1, 2, 3],print: function () {this.times.forEach(function (n) {console.log(this.name);});}
};obj.print()
如上代码没有任何输出,因为this指向为window
解决:
obj.print = function () {this.times.forEach(function (n) {console.log(this.name);}.bind(this));
};obj.print()
// 张三
// 张三
// 张三
利用 bind 方法,可以改写一些 JavaScript 原生方法的使用形式,以数组的 slice 方法为例。
[1, 2, 3].slice(0, 1) // [1]
// 等同于
Array.prototype.slice.call([1, 2, 3], 0, 1) // [1]
这样做的本质是在 [1, 2, 3] 上面调用 Array.prototype.slice 方法,因此可以用 call 方法表达这个过程,得到同样的结果。
call 方法实质上是调用 Function.prototype.call 方法,因此上面的表达式可以用 bind 方法改写。
var slice = Function.prototype.call.bind(Array.prototype.slice);
slice([1, 2, 3], 0, 1) // [1]
上面代码的含义就是,将 Array.prototype.slice 变成 Function.prototype.call 方法所在的对象,调用时就变成了 Array.prototype.slice.call。类似的写法还可以用于其他数组方法。
var push = Function.prototype.call.bind(Array.prototype.push);
var pop = Function.prototype.call.bind(Array.prototype.pop);var a = [1 ,2 ,3];
push(a, 4)
a // [1, 2, 3, 4]pop(a)
a // [1, 2, 3]
如果再进一步,将 Function.prototype.call 方法绑定到 Function.prototype.bind 对象,就意味着 bind 的调用形式也可以被改写。
function f() {console.log(this.v);
}var o = { v: 123 };
var bind = Function.prototype.call.bind(Function.prototype.bind);
bind(f, o)() // 123
上面代码的含义就是,将 Function.prototype.bind 方法绑定在 Function.prototype.call 上面,所以 bind 方法就可以直接使用,不需要在函数实例上使用。
手写bind
// 1. 需求:手写bind => bind位置(挂在那里) => Function.prototypeFunction.prototype.newBind = function() {// 2. bind是什么? const _this = this;const args = Array.prototype.slice.call(arguments);// args特点,第一项是新的this,第二项~最后一项函数传参const newThis = args.shift();// a. 返回一个函数return function() {// b. 返回原函数执行结果 c. 传参不变return _this.apply(newThis, args);}}
手写apply
Function.prototype.newApply = function(context) {// 边缘检测// 函数检测if (typeof this !== 'function') {throw new TypeError('Error');}// 参数检测context = context || window;// 挂载执行函数context.fn = this;// 执行执行函数let result = arguments[1]? context.fn(...arguments[1]): context.fn();// 销毁临时挂载delete context.fn;return result;}
this 的指向哪几种 ?
总结起来,this 的指向规律有如下几条: