this
this在JS中指的是当前对象的一个应用。只有在函数执行的时候才有意义(产生),每个函数在执行的时候都有自己的this指向。简而言之,谁调用该函数,该函数的this指向就是谁。
this指向的四种绑定规则
- 默认绑定规则
默认指向全局对象GO(浏览器环境中默认指向window)。 - 隐式绑定规则
谁调用就只指向谁(隐式丢失,参数赋值)。 - 显式绑定规则
call、apply、bind三个方法手动绑定this指向。 new绑定规则
new一个构造函数之后,构造函数内的this指向是new出来的实例对象。
1. 默认绑定规则
对函数进行独立调用时,此时没有使用其他绑定规则绑定this指向,this默认指向全局对象,浏览器环境下全局对象为。
function fn(){
console.log(this);
}
fn(); //函数独立调用,输出 window
2. 隐式绑定规则
使用函数时,用一个对象调用该函数,那么函数的this指向就是这个对象。
function fn(){
console.log(this);
}
var obj = {};
obj.fn(); //输出 obj
隐式绑定还会存在一个特殊现象:隐式丢失。
function fn(){
console.log(this);
}
var obj = {};
var fn2 = obj.fn;
fn2(); //隐式丢失,输出 window
3. 显式绑定规则
使用call、apply、bind对函数进行手动绑定this指向。
function fn(){
console.log(this);
}
var obj = {};
fn.call(obj); //输出obj
fn.apply(obj); //输出obj
fn.bind(obj)(); //输出obj
call、apply、bind 的异同点
- call和apply的使用方法相似,调用就执行一次函数;bind是返回一个绑定了指定this对象的新函数,需要手动调用。
- call和apply使用时,函数的参数也要和指定的this对象一起进行传参,call的参数需要依次输入,apply的参数是作为一个数组进行传参;由bind返回的是新函数,函数传参与普通函数执行一致。
function fn(a,b){
console.log(this , a + b);
}
var obj = {};
fn.call(obj,2,3); //输出 obj 2 3
fn.apply(obj,[5,6]) //输出 obj 5 6
const fn2 = fn.bind(obj);
fn(2,4); //输出 window 2 4
fn2(2,4): //输出 obj 2 4
PS:如果绑定的this对象为undefined或null,则为无效绑定,使用默认绑定规则。即,this指向window。
4. new绑定规则
使用new关键字实例化一个函数时,函数的this指向就是实例化出来的对象。
function fn(){
this.a = 3;
}
const obj = new fn();
console.log(obj.a); //输出 3
箭头函数
箭头函数是一种特殊的函数定义方式,特点是函数本身没有this,箭头函数的this其实是父作用域的this指向,简而言之,谁定义了箭头函数,箭头函数的this就指向谁。又因为箭头函数没有this,所以上述的四种绑定规则都不适用于箭头函数。
const fn = ()=>{
console.log(this);
}
function fn2(){
return ()=>{
console.log(whis);
}
}
const obj = {
foo: ()=>{
console.log(this);
}
};
fn(); // 输出 window 在全局作用域下定义的
obj.foo(); // 输出 window 在全局作用域下定义的,隐式绑定无效
fn.call(obj); // 输出 window ,显式绑定无效
new fn()l=; //报错, 箭头函数不允许作为构造函数使用,不能使用new关键字
const fn3 = obj.fun2();
fn3(); // 输出obj,如果fn2返回的不是箭头函数此处返回window,默认规则失效,fn3是在obj下定义的。
练手
- 例题1
var name = "window";
var obj1 = {
name: "1",
fn1: function () {
console.log(this.name);
},
fn2: () => console.log(this.name),
fn3: function () {
return function () {
console.log(this.name);
};
},
fn4: function () {
return () => console.log(this.name);
},
};
var obj2 = {
name: "2",
};
obj1.fn1();
obj1.fn1.call(obj2);
obj1.fn2();
obj1.fn2.call(obj2);
obj1.fn3()();
obj1.fn3().call(obj2);
obj1.fn3.call(obj2)();
obj1.fn4()();
obj1.fn4().call(obj2);
obj1.fn4.call(obj2)();
点击查看答案
obj1.fn1(); // 1 隐式绑定
obj1.fn1.call(obj2); // 2 显式绑定
obj1.fn2(); // window 箭头函数,隐式失效
obj1.fn2.call(obj2); // windown 箭头函数,显式失效
obj1.fn3()(); // window 默认绑定,函数独立调用
obj1.fn3().call(obj2); // 2 显式绑定
obj1.fn3.call(obj2)(); // window 默认绑定,函数独立调用
obj1.fn4()(); // 1 箭头函数 fn4的this指向obj1
obj1.fn4().call(obj2); // 1 显式失效 fn4的this指向obj1
obj1.fn4.call(obj2)(); // 2 fn4的this指向obj2
- 例题2
function Foo() {
getName = function () {
console.log(1);
};
return this;
}
Foo.getName = function () {
console.log(2);
};
Foo.prototype.getName = function () {
console.log(3);
};
var getName = function () {
console.log(4);
};
function getName() {
console.log(5);
}
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();
点击查看答案
Foo.getName(); //2 这里的getName是Foo的getName属性。
getName(); // 4 window的getName,先声明后赋值,声明时是5,赋值以后为4。
Foo().getName(); //1 返回的this为window,由于在Foo()里面赋值给了未声明的变量getName,此getName直接赋予window。
getName(); //1 在上一步被赋值。
new Foo.getName(); //2 等同new (Foo.getName)(),实例化的是Foo的getName属性。
new Foo().getName(); //3 等同 (new Foo()).getName(),实例化Foo,得到的getName是Foo原型链上的getName。
new new Foo().getName(); //3 等同new((new Foo()).getName)(), 里层的new得到的还是Foo原型链上的getName,外层的new实例化了里层的getName,故输出 3。