1.默认绑定
不带任何修饰的函数引用进行调用,使用默认绑定,直白的说独立函数调用使用默认绑定
1.1 非严格模式
var name = "张三";
function test() {
console.log(this.name);//this指向window 张三
};
test();
1.2 严格模式
var name = "张三";
function fn(){
'use strict';
console.log(this.name);//this指向undefined 报错
};
fn();
结论:默认绑定在非严格模式下this指向window,在严格模式下this指向undefined
2.隐式绑定
var obj = {
name: "张三",
fn: function () {
console.log(this.name)//张三
}
};
obj.fn();
fn函数被obj对象调用,所以this指向obj对象
var foo = {
name: "李四",
fun: function () {
console.log(this.name);//李四
}
};
var obj = {
name: "张三",
foo: foo
};
obj.foo.fun();
this指向最后一个调用的对象foo
<input id="name" type="text" value="name" value="Laruence" />
function showValue() {
alert(this);//input元素
};
document.getElementById('name').onclick = showValue;
document.getElementById(‘name’) 是dom对象,dom对象上添加一个onclick 方法,那么调用对象方法,所以this指向DOM元素。
结论:当函数调用有上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象(简单的说谁调用函数,this指向这个调用者),this在对象属性引用链中只有上一层或者说最后一层在调用位置中起作用(简单的说,当存在多个对象链式调用,this对象指上一个或者最后一个对象)
2.1 隐式丢失
被隐式绑定的函数会丢失绑定对象。也就是说会应用默认绑定,将this绑定到全局对象或者undefined(取决于是否是严格模式)。
var name="张三";
var obj = {
name: "李四",
fun: function (fn) {
fn();
}
};
function fn() {
console.log(this.name);//张三
};
obj.fun(fn);
fn函数当做参数被传递,此时this指向window
var name = "张三";
var obj = {
name: "李四",
fun: function (fn) {
console.log(this.name);//张三
}
};
var fn = obj.fun;
fn();
函数被赋值给fn变量,此时this指向window
结论:参数传递和变量赋值会导致隐式绑定中的this丢失,从而变成默认绑定
3. 显示绑定
通过call,apply,bind可以强行改变this的指向
,如果不懂的话可以点击这里
3.1 call,apply
var obj = {
name: "李四",
fun: function (age) {
console.log(this.name+age);
}
};
var obj1 = {
name : "李四",
}
obj.fun.call(obj1,"18岁");
obj.fun.apply(obj1,["18岁"]);
用过call,apply方法强行把this指向obj1
3.2 bind(硬绑定)
var name = '李四';
var obj = {
name: '张三',
};
function fn(age, sex) {
console.log(this.name + age + '岁了,性别' + sex);// 张三18岁了,性别男
};
var p = fn.bind(obj, '18');
p('男');
用过bind方法强行把this指向obj,bind会创建一个函数(称为绑定函数),创建一个新函数而不执行,这是bind和call与apply方法的一个重要差别,call和apply这两个方法都会立即执行函数,返回的是函数执行后的结果。而bind函数只创建一个新函数而不执行。
var name = '李四';
var obj = {
name: '张三',
};
var obj1={
name: '王五',
};
var obj2={
name: '赵六',
}
function fn(age, sex) {
console.log(this.name + age + '岁了');// 王二麻子18岁了,性别男
};
var p = fn.bind(obj, '18').bind(obj1).bind(obj2);
p();
fn.bind(obj, '18').call(obj1);
fn.bind(obj, '18').apply(obj2);
bind操作只有第一次绑定有效果,之后再次进行绑定,不会有效果
var name = '李四';
var obj = {
name: '张三',
};
function fn(age, sex) {
console.log(this.name + age + '岁了,性别' + sex);// 王二麻子18岁了,性别男
};
fn.prototype.name = "王二麻子";
var p = fn.bind(obj, '18');
var o=new p('男');
bind创建了一个新的函数,如果对这个函数进行new操作,那么this将指向实例出来的对象(o对象),因为o对象会继承fn的原型,所以o.name自己身上没有,就会去fn的原型上去找,this.name为王二麻子
结论:显示绑定call,apply,bind都会改this变指向,call和apply只是参数区别而已,bind创建一个新的函数而不执行,新函数被new之后,this指向会被再一次改变,指向实例化出来的实例, 并且bind只有第一次绑定有效!
4. new 绑定
首先要知道new操作具体干什么事情?
创建一个空对象,将它的引用赋给 this
这个对象和构造函数的原型链接起来(对象.__proto__= constructor.prototype)
this上添加属性和方法
如果有手动返回对象就返回此对象,没有的话默认返回this对象(实例对象)
function newTest() {
let constructor = Array.prototype.shift.call(arguments);//获取构造函数
let obj = Object.create(constructor.prototype);//创建新对象,并且 新对象.__proto__=constructor.prototype,这个新对象和构造函数的原型链接起来
let result = constructor.apply(obj, [...arguments]);//为新对象添加属性和方法
return typeof result == "object" ? result : obj //判断构造函数有没有手动返回对象,默认返回this(实例对象)
};
function fn(name, age) {
this.name = name;
this.age = age;
};
fn.prototype.get = function () {
console.log(this.name + this.age);//张三18
};
function newTest() {
let constructor = Array.prototype.shift.call(arguments);
let obj = Object.create(constructor.prototype);
let result = constructor.apply(obj, [...arguments]);
return typeof result == "object" ? result : obj;
}
var p = newTest(fn, '张三', 18);
p.get();
结论:new 调用函数,此时this指向实例化出来对象
5. 箭头函数绑定
var test = () =>
console.log(this);//window
test();
var test = () => {
"use strict";
console.log(this)//window
};
test();
不管严格还是非严格模式,默认全局环境下,this的指向都是window!
let obj={
name:"张三",
fn:()=>{
console.log(this.name);//this指向window
}
}
obj.fn();
结论:箭头函数的this指向是函数定义时候就绑定好的,也就是当前的词法作用域的this,即定义时上下文的this!(也可以理解去找父级作用域下的this指向)!
如果想知道更多细节点这里
总结论:this绑定优先级为: new绑定 >显示绑定 > 隐式绑定 > 默认绑定