1. this的指向
💡在函数中this到底取何值,是在函数真正被调用执行的时候确定的
,函数定义的时候确定不了,也就是说,this的指向完全取决于函数调用的位置
, 即this是在执行的时候被绑定的。
- 函数调用: 当一个函数不是一个对象的属性时,直接作为函数来调用时,this指向全局对象,
立即执行函数,默认的定时器等函数,this也是指向window
。 - 方法调用: 如果一个函数作为一个对象的方法来调用时,this指向这个对象。
- 构造函数调用: this指向这个用new新创建的对象。
- apply 、 call 和 bind 调用模式: 这三个方法都可以显示的指定调用函数的 this 指向。
- 箭头函数的this: 指向声明时所在作用域下 this 的值,即箭头函数的this去他的上级作用域下寻找,任何方法都改变不了他的指向
隐式绑定
// 严格模式:
// 'use strict'
// 1. 独立函数调用
function foo() {
console.log(this === window); //true
}
foo();
// 2. 方法调用
var obj = {
name: "zgc",
running: function () {
// 这里的上级作用域是window, 对象是数据类型, 不是代码块, 没有作用域
console.log(this);
},
};
obj.running(); //obj对象
var fn = obj.running;
fn(); // window对象
function bar() {
console.log(this);
}
var baz = {
name: "wf",
bar: bar,
};
baz.bar(); // baz对象
function test(fn) {
fn();
}
test(baz.bar); // window对象
// 3. 构造函数调用(new) 绑定
function Student(name) {
this.name = name;
console.log("构造函数", this); // {name: 'zgc'}
}
const stu = new Student("zgc");
// 4. 严格模式下, 独立函数调用this指向的是undefined
显式绑定(apply & call & bind)
- apply 、 call 、bind 三者都是用来改变函数的this对象的指向的;
- apply 、 call 、bind 三者第一个参数都是this要指向的对象,也就是想指定的上下文;
- apply是把参数放在一个数组里面作为它的第二个参数,而call、bind从第二个参数开始以参数列表的形式展现。
- bind则是返回改变了this指向的一个函数,便于稍后调用;apply 、call 则是立即调用
- bind方法通过传入一个对象,返回一个 this 绑定了传入对象的新函数, 这个函数的 this指向除了使用new 时会被改变,其他情况下都不会改变
var obj = {
name: "zgc",
};
function foo(name, age) {
console.log("foo", this, name, age);
}
// 让 foo的this指向obj
// 1. call
// 第一个参数, 绑定this
// 第二个参数开始, 以参数列表的形式展现
foo.call(obj, "zgc", 18); // foo {name: 'zgc'} zgc 18
// 2. apply
// 第一个参数, 绑定this
// 第二个参数, 以数组的形式传入额外的实参
foo.apply(obj, ["wf", 20]); // foo {name: 'zgc'} wf 20
// 3. bind
// 如果我们希望一个函数总是显示的绑定在一个对象上, 而不是每一次调用时再去绑定, 那么可以使用bind
// bind方法通过传入一个对象,返回一个 this 绑定了传入对象的新函数, 便于稍后调用
// 第一个参数, 绑定this
// 第二个参数开始, 以参数列表的形式展现
const baz = foo.bind(obj, "wlc", 22);
baz(); // foo {name: 'zgc'} wlc 22]
const bar = foo.bind(obj);
bar("cx", 24); // foo {name: 'zgc'} cx 24
内置函数的this指向
// JavaScript的内置函数
// 1. 定时器
setTimeout(function () {
console.log("定时器", this); // window
}, 1000);
// 2. 点击事件
var btn = document.querySelector("button");
// btn.onclick = function () {
// console.log("btn", this); // btn(<button>按钮</button>)
// };
btn.addEventListener("click", function () {
console.log("btn", this); // btn(<button>按钮</button>)
});
// 3. forEach等方法
//默认 window, 第二个参数可以绑定this
var names = ["zgc", "wf"];
var obj = { name: "zgc" };
names.forEach(function () {
console.log("forEach", this); // { name: "zgc" }
}, obj);
// 4. 补充: 立即执行函数this指向window
(function () {
console.log("立即执行函数", this);
})();
2. this绑定的优先级
function foo(age) {
console.log(this);
this.age = age;
}
var obj = {
name: "zgc",
foo: foo,
};
// 1. 隐式绑定(方法)的优先级高于默认绑定(独立函数调用)
obj.foo(18); // obj: {name: 'zgc', age: 18, foo: ƒ}
// 2. 显式绑定的优先级高于隐式绑定
var bar = {
name: "cx",
};
obj.foo.call(bar, 18); // bar: {name: 'cx', age: 18}
// 3. new不可以和call/apply一起使用, 但绑定的优先级高于bind绑定
var baz = obj.foo.bind(bar);
var test = new baz(18);
console.log(test); // test: {age: 18}
// 4. bind的优先级高于 apply/call
baz.call(obj); // bar: {name: 'cx', age: 18}
// 5: 在显式绑定中, 如果我们的第一个参数为null和undefined, 那么这个显式绑定会被忽略, 使用默认规则
obj.foo.call(null); // window
3. 箭头函数
💡ES6允许使用箭头(=>)定义函数,箭头函数多用于匿名函数的定义
- 如果形参只有一个,则小括号可以省略;
- 函数体如果只有一条语句,则花括号可以省略,函数的返回值为该条语句的执行结果,return省略
- 箭头函数其实本身并没有绑定this,,即箭头函数的this去他的上级作用域下寻找,任何方法都改变不了他的指向
- 箭头函数是匿名函数,不能作为构造函数实例化;
- 不能使用 arguments,使用reset参数
- 当省略花括号与return, 并且返回值是一个对象时, 对象必须包一个小括号
// 1. 箭头函数的定义
// var foo = (参数1, 参数2) => { 代码块 };
// 2. 当省略花括号与return, 并且返回值是一个对象时, 对象必须包一个小括号
var num = () => 1;
var obj = () => ({ name: "zgc" });
console.log(num(), obj()); // 1 {name: 'zgc'}
// 3. this的使用
// 箭头函数其实本身并没有绑定this,,即箭头函数的this去他的上级作用域下寻找
// 所以任何方法都无法改变该this的指向
var foo = () => {
console.log(this);
};
foo(); // window
var obj = {
name: "zgc",
running: () => {
// 这里的上级作用域是window, 对象是数据类型, 不是代码块, 没有作用域
console.log("箭头函数", this); // window
},
};
obj.running();
var obj = {
name: "wf",
running: function () {
// 这里的上级作用域是window, 对象是数据类型, 不是代码块, 没有作用域
console.log("普通函数", this); // obj
var bar = () => {
console.log("bar", this); // obj
};
return bar;
},
};
const fn1 = obj.running();
fn1();
var name = "xxxx";
var obj = {
name: "wf",
running: function () {
// 这里的上级作用域是window, 对象是数据类型, 不是代码块, 没有作用域
console.log("普通函数", this); // window
var bar = () => {
console.log("bar", this, name); // window xxxx
};
return bar;
},
};
const fn2 = obj.running;
fn2();
fn2()();