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()();