this相关知识点框架
this指向
上下文 + 作用域
执行上下文概念
作用域概念
作用域链概念
作用域链
let a = 'global';
console.log(a);
function course() {
let b = 'halo';
console.log(b);
session();
function session() {
let c = 'this';
console.log(c);
teacher();
function teacher() {
let d = 'yy';
console.log(d);
// 作用域查找
console.log(b);
}
}
}
course();
// 取消了全局、块级作用域
if(true){
let e = 1111;
console.log(e);
}
结果: global halo this yy halo 1111
this 上下文context
this是在执行时动态读取上下文决定的,不是在定义时决定
函数直接调用 - this 指向 window
function foo() {
console.log('函数内部的this:', this);
}
foo();
隐式绑定 - this指向调用堆栈的上一级
function fn() {
console.log(this)
console.log('隐式绑定:', this.a);
}
const obj = {
a: 1
}
obj.fn = fn;
obj.fn();
输出: {a: 1, fn: ƒ} 隐式绑定: 1
实战:
const foo = {
bar: 10,
fn: function() {
console.log(this.bar);
console.log(this);
}
}
let fn1 = foo.fn;
fn1();
//输出:
undefined
window
// 追问,如何改变指向
const o1 = {
text: 'o1',
fn: function() {
console.log(this)
return this.text;
}
}
const o2 = {
text: 'o2',
fn: function() {
return o1.fn();
}
}
const o3 = {
text: 'o3',
fn: function() {
let fn = o1.fn;
return fn();
}
}
console.log(o1.fn()); //{text: 'o1', fn: ƒ} o1
console.log(o2.fn()); //{text: 'o1', fn: ƒ} o1
console.log(o3.fn()); // Window undfined
-
- 在执行函数时,如果函数被上一级所调用,那么上下文即指向上一级
-
- 否则为全局孤立,指向window
追问: 现在我需要将console.log(o2.fn())结果是o2
// 1 - 人为干涉、改变this - bind/call/apply
// 2 - 不许改变this
const o1 = {
text: 'o1',
fn: function() {
return this.text;
}
}
const o2 = {
text: 'o2',
fn: o1.fn
}
// this指向最后调用他的对象,在fn执行时,函数挂到o2上即可
最终o2.fn()得到的结果就是'o2'
显式绑定(bind | apply | call)
function foo() {
console.log('函数内部的this:', this);
}
foo();
foo.call({a: 1});
foo.apply({a: 1});
const bindFoo = foo.bind({a: 1});
bindFoo();
追问:call、apply、bind的区别
相同点:
- 都是改变this指向的;
- 第一个参数都是this要指向的对象;
- 都可以利用后续参数传参; 不同点:
- 传参不同,call和bind的参数是依次传参,一一对应的
- 但apply只有两个参数,第二个参数为数组
- call和apply都是对函数进行直接调用,而bind方法返回的仍是一个函数;
new - this指向的是new之后得到的实例
class Course {
constructor(name) {
this.name = name;
console.log('构造函数中的this', this);
}
test() {
console.log('类方法中的this', this);
}
}
const course = new Course('this');
course.test();
追问: 异步方法中this有区别么
class Course {
constructor(name) {
this.name = name;
console.log('构造函数中的this', this);
}
test() {
console.log('类方法中的this', this);
}
asyncTest() {
console.log('异步方法外', this);
// setTimeout(() => {
// console.log('异步方法中的this', this); // 改成箭头函数后this指向course 指向与外层作用域this相同
// }, 100)
setTimeout(function() {
console.log('异步方法中的this', this);
}, 100)
}
}
const course = new Course('this');
course.test();
course.asyncTest();
打印输出:
- 构造函数中的this Course {name: 'this'}
- 类方法中的this Course {name: 'this'}
- 异步方法外 Course {name: 'this'}
- undefined
- 异步方法中的this Window
-
- 执行setTimeout时,传入匿名function执行,效果和全局执行函数效果相同
-
- 再追问,如何解决。把function改为无独立上下文的箭头函数即可
追问 bind原理 / 手写bind
-
- bind在哪里
function sum(a, b, c) {
console.log(a, b, c, this);
return a + b + c;
}
// 1. sum.bind - 在哪里 ? => Function.prototype
Function.prototype.newBind = function () {
// 2. bind 是什么?
// a.返回一个函数 b. 返回原函数执行结果 c. 传参不变
const _this = this;
// args特点: 第一项 - newThis, 第二项 ~ 最后一项 - 函数传参
const args = Array.prototype.slice.call(arguments);
const newThis = args.shift();
return function () {
return _this.apply(newThis, args);
}
}
-
- apply应用 - 多传参数组化
Math.max(2, 4, 5, 6);
const arr = [2, 4, 5, 6];
let max = Math.max.apply(this, arr); // 这里的this换成{}都可,相当于apply的副业
优先级 - new > 显式 > 隐式 > 默认
function fn() {
console.log(this);
}
const obj = {
fn
}
obj.fn() // obj 隐式绑定
// 显式 > 隐式
obj.fn.bind(111)() // Number {111} call,apply, bind为显示
funciton foo(a) {
this.a = a
}
const obj1 = {}
let bar = foo.bind(obj1);
bar(2);
console.log(obj1.a); // 2 foo里的this指向的是obj1
// new
let baz = new bar(3)
// new > 显式
console.log(obj1.a) //2
console.log(baz.a) //3
### 聊完了作用域和上下文,如何突破作用域的束缚
### 闭包: 一个函数和他周围状态的引用捆绑在一起的组合
#### 函数作为返回值场景
```js
function mail() {
let content = '信';
return function() {
console.log(content);
}
}
const envelop = mail();
envelop();
- 函数外部获取到了函数作用域内的变量值
函数作为参数
let content;
function envelop(fn) {
content = 1;
fn();
}
function mail() {
console.log(content);
}
envelop(mail); // 1
函数嵌套
let counter = 0;
function outerFn() {
function innerFn() {
counter++;
console.log(counter);
}
return innerFn;
}
outerFn()() // 1
事件处理(异步执行)的闭包
let lis = document.getElementsByTagName('li');
for (var i = 0; i< lis.length; i++) {
lis[i].onclick = function() {
console.log(i);
}
setTimeout(function() {
console.log(i); // 6个6 因为上层作用域的变量i已经循环完成
}, 100)
}
for (var i = 0; i< lis.length; i++) {
(function(i) {
lis[i].onclick = function() {
console.log(i);
}
setTimeout(function() {
console.log(i); // 1,2,3,4,5 拿到的是闭包声明的时候的作用域的i,这时候已经传进来了,不会被外层循环所累加
}, 100)
})(i);
}
追问:
立即执行嵌套
(function immediateA(a) {
return (function immediateB(b) {
console.log(a, b); // 0 1
})(1);
})(0);
立即执行遇上块级作用域 !!!!!!!!!
let count = 0;
(function immediate() {
if (count === 0) {
let count = 1;
console.log(count); // 1
}
console.log(count); // 0
})();
拆分执行 多个闭包
function createIncrement() {
let count = 0;
function increment() {
count++;
console.log(count);
}
let message = `count is ${count}`;
function log() {
console.log(message);
}
return [increment, log];
}
const [increment, log] = createIncrement();
increment(); // 1
increment(); // 2
increment(); // 3
log(); // count is 0
实现私有变量
function createStack() {
return {
items: [],
push(item) {
this.items.push(item);
}
}
}
const stack = {
items: [],
push: function() {}
}
function createStack() {
const items = [];
return {
push(item) {
items.push(item);
}
}
}
闭包概念
闭包习题训练
箭头函数的 this 指向哪⾥?
箭头函数并没有属于⾃⼰的this,它所谓的this是捕获其所在上下⽂的 this 值,作为⾃⼰的this值
// ES6
const obj = {
getArrow() {
return () => {
console.log(this === obj); // true
};
}
}
bind() 连续调用多次,this的绑定值是什么呢?
答案是,两次都仍将输出 3 ,而非期待中的 4 和 5 。
原因是,在Javascript中,多次 bind() 是无效的。
以下代码的输出是什么?
window
obj
window
window