this其他补充
内置函数的绑定思考
01_一些函数的this分析
// 1.setTimeout原理
// function hySetTimeout(fn, duration) {
// fn.call(window)
// }
// hySetTimeout(function() {
// console.log(this) // window
// }, 3000)
// setTimeout(function() {
// console.log(this) // window
// }, 2000)
// 2.监听点击
// 这个只能调用一次onclick,再次调用会被覆盖
const boxDiv = document.querySelector(".box");
boxDiv.onclick = function () {
console.log(this);
};
// 这个只能调用多次onclick,用会存储到一个数组里面
boxDiv.addEventListener("click", function () {
console.log(this);
});
boxDiv.addEventListener("click", function () {
console.log(this);
});
boxDiv.addEventListener("click", function () {
// 原理 :fn。call(boxDiv)
console.log(this);
});
// 3.数组.forEach/map/filter/find
// forEach的第一个参数是函数,第二个参数是this的绑定
var names = ["abc", "cba", "nba"];
names.forEach(function (item) {
console.log(item, this);
}, "abc");
// map的第一个参数是函数,第二个参数是this的绑定
names.map(function (item) {
console.log(item, this);
}, "cba");
规则优先级
02_优先级-显示高于隐式绑定
var obj = {
name: "obj",
foo: function () {
console.log(this);
},
};
obj.foo();
// 1.call/apply的显示绑定高于隐式绑定
// obj.foo.apply('abc')
// obj.foo.call('abc')
// 2.bind的优先级高于隐式绑定
// var bar = obj.foo.bind("cba")
// bar()
// 3.更明显的比较
function foo() {
console.log(this);
}
var obj = {
name: "obj",
foo: foo.bind("aaa"),
};
obj.foo(); //绑定显示绑定 [String: 'aaa']
03_优先级-new高于隐式绑定
var obj = {
name: "obj",
foo: function () {
console.log(this);
},
};
// new的优先级高于隐式绑定
var f = new obj.foo();
04_优先级-new高于显示绑定
// 结论: new关键字不能和apply/call一起来使用
// new的优先级高于bind
function foo() {
console.log(this);
}
var bar =
bar(); //aaa
var obj = new bar();
// new绑定 > 显示绑定(apply/call/bind) > 隐式绑定(obj.foo()) > 默认绑定(独立函数调用)
this规则之外 – 忽略显示绑定
05_特殊绑定-忽略显示绑定
function foo() {
console.log(this);
}
foo.apply("abc");
foo.apply({
});
// apply/call/bind: 当传入null/undefined时, 自动将this绑定成全局对象
// foo.apply(null);
// foo.apply(undefined);
var bar = foo.bind(null);
bar();
this规则之外 - 间接函数引用
06_特殊绑定-间接函数引用
// 争论: 代码规范 ;
var obj1 = {
name: "obj1",
foo: function () {
console.log(this);
},
};
var obj2 = {
name: "obj2",
};
// obj2.bar = obj1.foo
// obj2.bar() //obj2
// 分号也是很关键的 ,不加分号会报错 会当成一行代码执行
(obj2.bar = obj1.foo)(); // 浏览器中是window node中是global
0.测试代码
// 你不知道的JavaScript
function foo(el) {
console.log(el, this.id);
}
var obj = {
id: "awesome",
}[
// 分号也是很关键的 ,不加分号会报错 -和obj{ id:'awesome'}[1,2,3].forEach(foo,obj)一起执行报错
(1, 2, 3)
].forEach(foo, obj);
var nums = [1, 2, 3];
nums.forEach(foo, obj);
箭头函数 arrow function
箭头函数的编写优化
07_箭头函数的使用解析
// 1.编写箭头函数
// 1> (): 参数
// 2> =>: 箭头
// 3> {}: 函数的执行体
var foo = (num1, num2, num3) => {
console.log(num1, num2, num3);
var result = num1 + num2 + num3;
console.log(result);
};
function bar(num1, num2, num3) {
}
// 高阶函数在使用时, 也可以传入箭头函数
var nums = [10, 20, 45, 78];
nums.forEach((item, index, arr) => {
});
// 箭头函数有一些常见的简写:
// 简写一: 如果参数只有一个, ()可以省略
nums.forEach((item) => {
console.log(item);
});
// 简写二: 如果函数执行体只有一行代码, 那么{}也可以省略
// 强调: 并且它会默认将这行代码的执行结果作为返回值
nums.forEach((item) => console.log(item));
var newNums = nums.filter((item) => item % 2 === 0);
console.log(newNums);
// filter/map/reduce
var result = nums
.filter((item) => item % 2 === 0)
.map((item) => item * 100)
.reduce((preValue, item) => preValue + item);
console.log(result);
// 简写三: 如果一个箭头函数, 只有一行代码, 并且返回一个对象, 这个时候如何编写简写
// var bar = () => {
// return { name: "why", age: 18 }
// }
// 对象形式的简写,是个整体,告诉浏览器不是逻辑代码块而是一个对象
var bar = () => ({
name: "why", age: 18 });
this规则之外 – ES6箭头函数
08_箭头函数的this获取
// 1.测试箭头函数中this指向
// var name = "why"
var foo = () => {
console.log(this);
};
// 箭头函数没有this,唯一确定this的方式是从上级作用域中寻找
foo();
var obj = {
foo: foo };
obj.foo();
foo.call("abc");
// 2.应用场景
var obj = {
data: [],
getData: function () {
// 发送网络请求, 将结果放到上面data属性中
// 在箭头函数之前的解决方案
// var _this = this
// setTimeout(function() {
// var result = ["abc", "cba", "nba"]
// _this.data = result
// }, 2000);
// 箭头函数之后 this的指向在上级作用域中寻找,这里面的this就是指向getDate函数,因为getDate函数是setTimeout的上级作用域
// getData是普通函数 又因为obj.getData()通过隐式绑定 绑定到obj,所以这里面的this就变成了obj对象,就可以调用date了
setTimeout(() => {
var result = ["abc", "cba", "nba"];
this.data = result;
console.log(this);
}, 2000);
},
};
obj.getData();
3.如果getData也是一个箭头函数,那么setTimeout中的回调函数中的this指向window {
}
// 1.测试箭头函数中this指向
// var name = "why"
var foo = () => {
console.log(22, this);
var boo = () => {
console.log(11, this);
};
boo();
};
// 箭头函数没有this,唯一确定this的方式是从上级作用域中寻找
foo();
// 2.应用场景
var obj = {
data: [],
aaa: [],
getData: () => {
console.log(this);
// 发送网络请求, 将结果放到上面data属性中
// 在箭头函数之前的解决方案
// var _this = this
// setTimeout(function() {
// var result = ["abc", "cba", "nba"]
// _this.data = result
// }, 2000);
// 箭头函数之后 this的指向在上级作用域中寻找,这里面的this就是指向getDate函数,因为getDate函数是setTimeout的上级作用域
// getData是箭头函数 obj.getData()通过隐式绑定 不起效果,所以这里面的this依然是window node中是{}
setTimeout(() => {
var result = ["abc", "cba", "nba"];
//这里的this是最上层的作用域window node中是{}
this.data = result;
console.log(this);
}, 2000);
},
};
obj.getData();
面试题一:
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(); // person: 隐式调用 (person.sayName)()其实相当于就是person.sayName();
(b = person.sayName)(); // 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 () {
return () => {
console.log(this.name);
};
},
};
var person2 = {
name: "person2" };
// person1.foo1(); // person1(隐式绑定)
// person1.foo1.call(person2); // person2(显示绑定优先级大于隐式绑定)
// person1.foo2(); // window(箭头函数没有this,不绑定作用域,上层作用域是全局) node中是undefined
// person1.foo2.call(person2); // window node中是undefined
// person1.foo3()(); // window(独立函数调用) person1.foo3()是个结果没有调用主体
// person1.foo3.call(person2)(); // window(独立函数调用)--重点是只看最终的this绑定是谁,这里是独立函数的调用
// person1.foo3().call(person2); // person2(最终调用返回函数式, 使用的是显示绑定)
// person1.foo4()(); // person1(箭头函数不绑定this, 上层作用域this是person1)
// person1.foo4.call(person2)(); // person2(上层作用域被显示的绑定了一个person2)
// person1.foo4().call(person2); // person1(上层找到person1)person1.foo4()使上层作用域变成person1,又因为箭头函数没有this,不绑定call,而是从上层作用域中找
面试题三:
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);
};
});
}
// 函数每次new的时候都会创建一个新对象 每次的传入的this也是根据自己传入的参数自定义的this
var person1 = new Person("person1");
var person2 = new Person("person2");
person1.foo1(); // person1
person1.foo1.call(person2); // person2(显示高于隐式绑定)
person1.foo2(); // person1 (上层作用域中的this是person1)
person1.foo2.call(person2); // person1 (上层作用域中的this是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
var obj = {
name: "obj",
foo: function () {
},
};
面试题四:
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)是改变foo1的this指向为person2 但是foo1返回值为一个独立函数 独立函数的调用为window
person1.obj.foo1().call(person2); // person2 隐式绑定person2
person1.obj.foo2()(); // obj 上层作用域 主要看的返回值这个函数的上层作用域foo2是被谁调取的 这里是obj 调取的
person1.obj.foo2.call(person2)(); // person2 上层作用域 主要看的返回值这个函数的上层作用域foo2通过call改变this成为person2 所以是person2
person1.obj.foo2().call(person2); // obj 上层作用域是foo2通过隐式绑定变成obj, 箭头函数没有this 看上层作用域的指向
//
// 上层作用域的理解
// var obj = {
// name: "obj",
// foo: function() {
// 上层作用域是全局
// }
// }
// function Student() {
// this.foo = function() {
// 上层作用域是function Student
// }
// }