1 this的绑定规则
2 apply/call/bind
3 this绑定优先级
4 绑定之外的情况
5 箭头函数的使用
6 this面试题分
<script> // 定义函数 function foo(name) { console.log("foo函数:", this) } // 1.方式一: 直接调用 这里的this指向window // foo() // 2.方式二: 通过对象调起 这里的this指向obj var obj = { name: "why" } obj.aaa = foo obj.aaa() </script>
this绑定规则一-默认绑定
<script> // "use strict" // 定义函数 // 1.普通的函数被独立的调用 这里的this指向window function foo() { console.log("foo:", this) } foo() // 2.函数定义在对象中, 但是独立调用 var obj = { name: "why", bar: function() { console.log("bar:", this) } } // 这里的this指向window var baz = obj.bar baz() // 3.高阶函数 这里的this指向window function test(fn) { fn() } test(obj.bar) // 4.严格模式下, 独立调用的函数中的this指向的是undefined </script>
this绑定规则二-隐式绑定
<script> // 你不知道的JavaScript(上中下) // 隐式绑定 这里的this指向obj function foo() { console.log("foo函数:", this) } var obj = { bar: foo } obj.bar() </script>
this绑定规则三-new绑定
<script> /* 1.创建新的空对象 2.将this指向这个空对象 3.执行函数体中的代码 4.没有显示返回非空对象时, 默认返回这个对象 */ function foo() { this.name = "why" console.log("foo函数:", this) } new foo() </script>
this绑定规则四-显式绑定
<script> // 显式绑定 var obj = { name: "why" } function foo() { console.log("foo函数:", this) } // 执行函数, 并且函数中的this指向obj对象 // obj.foo = foo // obj.foo() // 执行函数, 并且强制this就是obj对象 foo.call(obj) foo.call(123) foo.call("abc") </script>
额外函数补充-apply-call
<script> // call/apply function foo(name, age, height) { console.log("foo函数被调用:", this) console.log("打印参数:", name, age, height) } // ()调用 // foo("why", 18, 1.88) // apply // 第一个参数: 绑定this // 第二个参数: 传入额外的实参, 以数组的形式 // foo.apply("apply", ["kobe", 30, 1.98]) // call // 第一个参数: 绑定this // 参数列表: 后续的参数以多参数的形式传递, 会作为实参 foo.call("call", "james", 25, 2.05) </script>
额外函数补充-bind
<script> function foo(name, age, height, address) { console.log("foo:", this) console.log("参数:", name, age, height, address) } var obj = { name: "why" } // 需求: 调用foo时, 总是绑定到obj对象身上(不希望obj对象身上有函数) // 1.bind函数的基本使用 // var bar = foo.bind(obj) // bar() // this -> obj // 2.bind函数的其他参数(了解) var bar = foo.bind(obj, "kobe", 18, 1.88) bar("james") </script>
内置函数的调用绑定
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <button>按钮</button> <script> // 内置函数(第三方库): 根据一些经验 // 1.定时器 这里的this指向window // setTimeout(function() { // console.log("定时器函数:", this) // }, 1000) // 2.按钮的点击监听 这里的this指向button标签 // var btnEl = document.querySelector("button") // btnEl.onclick = function() { // console.log("btn的点击:", this) // } // btnEl.addEventListener("click", function() { // console.log("btn的点击:", this) // }) // // 3.forEach 这里的this指向aaaa;如果不绑定aaaa的话指向的是names数组对象 var names = ["abc", "cba", "nba"] names.forEach(function(item) { console.log("forEach:", this) }, "aaaa") </script> </body> </html>
this绑定规则优先级
优先级从高到底:1、new;2、bind;3、call/apply;4、隐式绑定;5、默认绑定。
<script> // function foo() { // console.log("foo:", this) // } // 比较优先级: // 1.显式绑定绑定的优先级高于隐式绑定 // 1.1.测试一:apply高于默认绑定 // var obj = { foo: foo } // obj.foo.apply("abc") // obj.foo.call("abc") // 1.2.测试二:bind高于默认绑定 // var bar = foo.bind("aaa") // var obj = { // name: "why", // baz: bar // } // obj.baz() // 2.new绑定优先级高于隐式绑定 // var obj = { // name: "why", // foo: function() { // console.log("foo:", this) // console.log("foo:", this === obj) // } // } // new obj.foo() // 3.new/显式 // 3.1. new不可以和apply/call一起使用 // 3.2. new优先级高于bind // function foo() { // console.log("foo:", this) // } // var bindFn = foo.bind("aaa") // new bindFn() // 4.bind/apply优先级 // bind优先级高于apply/call function foo() { console.log("foo:", this) } var bindFn = foo.bind("aaa") bindFn.call("bbb") </script>
this绑定之外的情况
在严格模式下:显式绑定this的话,就直接指向是你绑定的内容比如null和undefined。
非严格模式:显式绑定的this,如果是null和undefined会被改为window。字符串等会包在一个对象里面。
<script> // 1.情况一: 显式绑定null/undefined, 那么使用的规则是默认绑定 // function foo() { // console.log("foo:", this) // } // 下面三个的this都是指向window,如果是严格模式则显示是abc和null、undefined // foo.apply("abc") // foo.apply(null) // foo.apply(undefined) // 2.情况二: 间接函数引用 var obj1 = { name: "obj1", foo: function() { console.log("foo:", this) } } var obj2 = { name: "obj2" }; // {}[]()连续的括号要加分号,否则浏览器不能识别 // obj2.foo = obj1.foo // obj2.foo() // 下面这行代码的this指向window (obj2.foo = obj1.foo)() </script>
箭头函数的使用
注意箭头函数是没有绑定this和arguments的;
且不能和new一起使用
基本使用:
<script> // 1.之前的方式 function foo1() {} var foo2 = function(name, age) { console.log("函数体代码", this, arguments) console.log(name, age) } // 2.箭头函数完整写法 var foo3 = (name, age) => { console.log("箭头函数的函数体") console.log(name, age) } // 3.箭头函数的练习 // 3.1. forEach var names = ["abc", "cba", "nba"] names.forEach((item, index, arr) => { console.log(item, index, arr) }) // 3.2. setTimeout setTimeout(() => { console.log("setTimeout") }, 3000) </script>
箭头函数的简写
单个参数可以省略()
一行执行体可以省略{}
一行执行体如果返回的是对象类型,则需要在对象的{}外面加一层()
<script> var names = ["abc", "cba", "nba"] var nums = [20, 30, 11, 15, 111] // 1.优化一: 如果箭头函数只有一个参数, 那么()可以省略 // names.forEach(item => { // console.log(item) // }) // var newNums = nums.filter(item => { // return item % 2 === 0 // }) // 2.优化二: 如果函数体中只有一行执行代码, 那么{}可以省略 // names.forEach(item => console.log(item)) // 一行代码中不能带return关键字, 如果省略, 需要带return一起省略(下一条规则) // var newNums = nums.filter(item => { // return item % 2 === 0 // }) // 3.优化三: 只有一行代码时, 这行代码的表达式结果会作为函数的返回值默认返回的 // var newNums = nums.filter(item => item % 2 === 0) // var newNums = nums.filter(item => item % 2 === 0) // 4.优化四: 如果默认返回值是一个对象, 那么这个对象必须加() // 注意: 在react中我会经常使用 redux // var arrFn = () => ["abc", "cba"] // var arrFn = () => {} // 注意: 这里是{}执行体 // var arrFn = () => ({ name: "why" }) // console.log(arrFn()) // 箭头函数实现nums的所有偶数平方的和 var nums = [20, 30, 11, 15, 111] var result = nums.filter(item => item % 2 === 0) .map(item => item * item) .reduce((prevValue, item) => prevValue + item) console.log(result) </script>
箭头函数中的this使用
由于箭头函数本身没有this,所以箭头函数里面的this会指向箭头函数外面一层的作用域的this,如果外面一层也是箭头函数,则继续向外查找作用域。对象类型的{ }花括号不是作用域,所以this在查找的时候直接跳过这里。
箭头函数使用显式绑定this也没有用。
箭头函数的this和参数的查找顺序很像。
<script> // 1.普通函数中是有this的标识符 // function foo() { // console.log("foo", this) // } // foo() // foo.apply("aaa") // 2.箭头函数中, 压根没有this // var bar = () => { // console.log("bar:", this) // } // bar() // 通过apply调用时, 也是没有this // bar.apply("aaaa") // console.log("全局this:", this) // var message = "global message" // 3.this的查找规则 // var obj = { // name: "obj", // foo: () => { // var bar = () => { // console.log("bar:", this) // } // return bar // } // } // var fn = obj.foo() // fn.apply("bbb") </script>
箭头函数中的this应用
<script> // 网络请求的工具函数 function request(url, callbackFn) { var results = ["abc", "cba", "nba"] callbackFn(results) } // 实际操作的位置(业务) var obj = { names: [], network: function() { // 1.早期的时候 // var _this = this // request("/names", function(res) { // _this.names = [].concat(res) // }) // 2.箭头函数写法 request("/names", (res) => { this.names = [].concat(res) }) } } obj.network() //影式绑定了obj对象 console.log(obj) </script>
var name = "window"; var person = { name: "person", sayName: function () { console.log(this.name); } }; function sayName() { var sss = person.sayName; sss(); // 绑定: 默认绑定, window -> window person.sayName(); // 绑定: 隐式绑定, person -> person (person.sayName)(); // 绑定: 隐式绑定, person -> person (b = person.sayName)(); // 术语: 间接函数引用, window -> window } sayName();
var name = 'window' // {} -> 对象 // {} -> 代码块 var person1 = { name: 'person1', foo1: function () { console.log(this.name) }, foo2: () => console.log(this.name), foo3: function () { return function () { console.log(this.name) } }, foo4: function () { // console.log(this) // 第一个表达式this -> person1 // console.log(this) // 第二个表达式this -> person2 // console.log(this) // 第三个表达式this -> person1 return () => { console.log(this.name) } } } var person2 = { name: 'person2' } // 开始题目: person1.foo1(); // 隐式绑定: person1 person1.foo1.call(person2); // 显式绑定: person2 person1.foo2(); // 上层作用域: window person1.foo2.call(person2); // 上层作用域: window person1.foo3()(); // 默认绑定: window person1.foo3.call(person2)(); // 默认绑定: window person1.foo3().call(person2); // 显式绑定: person2 person1.foo4()(); // person1 person1.foo4.call(person2)(); // person2 person1.foo4().call(person2); // person1
var name = 'window' /* 1.创建一个空的对象 2.将这个空的对象赋值给this 3.执行函数体中代码 4.将这个新的对象默认返回 */ function Person(name) { this.name = name this.foo1 = function () { console.log(this.name) }, this.foo2 = () => console.log(this.name), this.foo3 = function () { return function () { console.log(this.name) } }, this.foo4 = function () { return () => { console.log(this.name) } } } // person1/person都是对象(实例instance) var person1 = new Person('person1') var person2 = new Person('person2') // 面试题目: person1.foo1() // 隐式绑定: person1 person1.foo1.call(person2) // 显式绑定: person2 person1.foo2() // 上层作用域查找: person1 person1.foo2.call(person2) // 上层作用域查找: person1 person1.foo3()() // 默认绑定: window person1.foo3.call(person2)() // 默认绑定: window person1.foo3().call(person2) // 显式绑定: person2 person1.foo4()() // 上层作用域查找: person1(隐式绑定) person1.foo4.call(person2)() // 上层作用域查找: person2(显式绑定) person1.foo4().call(person2) // 上层作用域查找: person1(隐式绑定)
因为这个函数是构造函数的形式,所以new出来的是对象类型的,类似对象里面的key和value。
{
name:'person1',
foo1:函数的内存地址
}
var name = 'window' /* 1.创建一个空的对象 2.将这个空的对象赋值给this 3.执行函数体中代码 4.将这个新的对象默认返回 */ function Person(name) { this.name = name this.obj = { name: 'obj', foo1: function () { return function () { console.log(this.name) } }, foo2: function () { return () => { console.log(this.name) } } } } var person1 = new Person('person1') var person2 = new Person('person2') person1.obj.foo1()() // 默认绑定: window person1.obj.foo1.call(person2)() // 默认绑定: window person1.obj.foo1().call(person2) // 显式绑定: person2 person1.obj.foo2()() // 上层作用域查找: obj(隐式绑定) person1.obj.foo2.call(person2)() // 上层作用域查找: person2(显式绑定) person1.obj.foo2().call(person2) // 上层作用域查找: obj(隐式绑定)