内容回顾
一. this的绑定规则
1.1. 绑定规则
- 默认绑定
- 普通的函数被独立调用了
function foo() { console.log("foo", this) } foo() // window
- 函数定义在对象中, 但是独立调用
// 2. 函数定义在对象中, 但是独立调用
var obj = {
name: "孙悟空",
bar: function() {
console.log("bar", this)
}
}
var baz = obj.bar
baz() // 这种属于独立函数调用 this指向的是window
- 高阶函数
function test(fn) {
fn()
}
test(obj.bar)
- 严格模式下, 独立调用的函数中的this指向的是undefined
"use strict" // 这样写就是严格模式
- 隐式绑定
- JavaScript中的函数时可以当做一个类的构造函数来使用, 也就是使用new关键字
function foo() {
console.log("foo函数", this)
}
var obj = {
bar: foo
}
obj.bar()
- 显式绑定
var obj = {
name: "孙悟空"
}
function foo() {
console.log("foo函数", this)
}
// 执行函数, 并且函数this指向obj对象
// obj.foo = foo
// obj.foo()
// 执行函数. 并且强制this就是obj对象
foo.call(obj)
foo.call("abc") // 包装类对象
foo.call(123) // 包装类对象
foo.call(undefined) // 默认没有包装类型绑定到window
- new绑定
- 创建一个新对象
- 将this指向空对象
- 执行函数体中的代码
- 没有显示非空对象时, 默认返回这个对象
function foo() {
this.name = "孙悟空"
console.log("foo函数", this)
}
new foo()
1.2. 显式绑定
- apply/call
- bind
- JavaScript所有函数都可以使用call和apply方法
- 第一个参数是相同的, 要传入一个对象
- 这个对象的作用是什么呢? 就是给this准备的
- 在调用这个函数时, 会将this绑定到这个传入的对象上
- 后面的参数, apply为数组, call为参数列表
- 第一个参数是相同的, 要传入一个对象
function foo(name, age, height) {
console.log("foo函数被调用", this)
console.log("打印参数", name, age, height)
}
// apply
foo.apply("apply", ["猪八戒", 28, 1.98])
// acll
foo.call("call", "沙和尚", 38, 2.18)
- 如果我们希望一个函数总是显示绑定到一个对象身上, 可以怎么做呢?
- 使用bind()方法, bind()方法创建一个新的绑定函数
- 绑定函数是一个exotic function object(怪异函数对象 ECMAScript 2015中的术语)
function foo(name, age, height, address) {
console.log("foo函数", this)
console.log("参数", name, age, height, address)
}
var obj = {name: "孙悟空"}
// 2. bind函数的其他参数
var bar = foo.bind(obj, "猪八戒", 28, 1.98)
bar("沙悟净")
1.3. 内置函数的规则
- 经验
// 1. 定时器
setTimeout(function() {
console.log("定时器函数", this) // window
}, 1000)
// 2. 按钮的点击监听
var btnEl = document.querySelector("button")
btnEl.onclick = function() {
console.log("按钮的点击", this) // btn
}
btnEl.addEventListener("click", function() {
console.log("btnEl点击", this) // btn
})
// 3. forEach 第二个参数绑定的this
var names = ["abc", "bca", "nba"]
names.forEach(function(item) {
console.log("forEach", this) // 有你好参数绑定的是String"你好" 没有就是window
}, "你好")
1.4. 规则的优先级
-
new (优先级最高)
-
bind (其次)
-
apply/call (在其次)
-
隐式绑定 (在其次)
-
默认绑定 (最后)
- new绑定优先级高于隐式绑定
- new 不可以和apply和call一起使用
- 显示绑定优先级高于隐式绑定
- bind的优先级高于apply
- bind的优先级高于call
- new绑定优先级高于隐式绑定
1.5. 规则之外的情况
- undefined/null
- 间接函数引用(了解)
- 如果在显示绑定中, 我们传入一个undefined或者null
-
那么这个显示绑定会被忽略, 使用默认规则
-
创建一个函数的间接引用, 这种情况使用默认绑定规则
- 显示绑定使用null或者undefined使用的是默认绑定
-
function foo() {
console.log("foo函数", this)
}
foo.apply("abc")
foo.apply(undefined) // window
foo.apply(null)
- 间接函数引用
var obj1 = {
name: 'obj1',
foo: function() {
console.log("foo", this)
}
}
var obj2 = {
name: "obj2"
};
// obj2.foo = obj1.foo
// obj2.foo()
// 前面需要加上逗号.
;(obj2.foo = obj1.foo)() // window 默认绑定
二. 箭头函数的使用
2.1. 箭头函数的写法
- 基本写法
- 优化写法
- 只有一个参数时, 可以省略()
- 只有一行代码时, 可以省略{}
- 只要一行代码时, 表达式的返回值会作为箭头函数默认返回值, 所以可以省略return
- 如果箭头函数默认返回的是对象, 在省略{}的时候, 对象必须使用()包裹 () => ({name: “why”})
// 箭头函数完整写法
var foo3 = (name, age) => {
console.log("箭头函数的函数体")
console.log(name, age)
}
- 箭头函数练习
// 箭头函数实现nums所有偶数平方和 var nums = [20, 30, 11, 15, 111] var result = num.filte(item => item % 2 === 0) .map(item => item * item) .reduce((prvValue, item) => prvValue + item)
2.2. 箭头函数中的this
- 箭头函数是没有绑定this
- this的查找规则:
- 去上层作用域中查找this
- 直到找到全局this
var bar = () => {
console.log("bar", this)
}
bar()
通过apply调用也没有this的
bar.apply("勇敢牛牛")
var obj = {
name: "obj",
foo: () => {
var bar = () => {
console.log("bar", this)
}
return bar
}
}
var fn = obj.foo()
fn.apply("你好")
2.3. 箭头函数this应用
- 模拟网络请求
- 不好理解: 回调(好好理解一下)
- 函数传来传去
// 网络请求工具函数
function requset(url, callback) {
var result = ["abc", "cba", "nba"]
callback(result)
}
// 实际操作业务代码
var obj = {
names: [],
network: function () {
// 1. 早期的做法
// var _this = this
// requset("/names", function(res) {
// _this.names = [].concat(res)
// })
// 2. 箭头函数写法
requset("/names", (res) => {
this.names = [].concat(res)
})
}
}
obj.network()
console.log(obj)
this面试题
var name = "window";
var person = {
name: "person",
sayName: function () {
console.log(this.name);
}
};
function sayName() {
var sss = person.sayName;
sss(); // window 独立函数调用 默认绑定
person.sayName(); // 隐式绑定: person
(person.sayName)(); // 隐式绑定: prerson和上面一样
(b = person.sayName)(); // 间接函数引用 独立函数 ~window
}
sayName();
var name = 'window'
var person1 = {
name: 'person1',
foo1: function () {
console.log(this.name)
},
foo2: () => console.log(this.name), // 找上层作用域 因为 Person1是对象, 没有作用域 所以是window
foo3: function () {
return function () {
console.log(this.name)
}
},
foo4: function () {
return () => {
console.log(this.name)
}
}
}
var person2 = { name: 'person2' }
person1.foo1(); // 隐式绑定 person1
person1.foo1.call(person2); // 显示绑定 person2
person1.foo2(); // 上层作用域 window
person1.foo2.call(person2); // 因为没有this call绑定也没用 还是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'
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)
}
}
}
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因为拿到一个函数直接调用 相当于 fn()
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'
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