整体情况
- 全局环境下指向window。严格模式下需要严格写清函数的调用环境
- 隐式绑定,谁调用指向谁,链式调用指向最后一个调用的。引用类型都可以做调用
- 显示绑定,call/apply/bind可以改变this指向
- new绑定,this指向由构造函数改为新建的对象
- 箭头函数,没有this,向外层找,看外层的this
注意点
做完题再回来看,理解了就可以了
- 箭头函数没有this,去它的上一层找,不在乎是谁调用
- 箭头函数去上一层找this时,在执行前就已经确定,不会在调用后更改,比如通过call调用不会改变
- {}不构成独立作用域
- 引用类型都可以作为调用者
- 匿名函数独立调用,指向window
- let声明的变量不会成为window的属性
- fn.call(null) 传null指向window
- function声明的提升优先级高于 var声明的变量
- 如果同一作用域中存在同名的 function和 var,函数的声明会覆盖变量的声明
- var只提升声明,不提升赋值
- 全局作用域this指向window
- setTimeout的回调函数是被 JavaScript 引擎直接调用的,独立调用的。匿名函数this指向window,箭头函数看外层。
- 立即执行函数(IIFE)是独立调用的,没有绑定到任何对象。普通函数在独立调用时,this默认指向全局对象(window),在严格模式下,独立调用的函数 this是 undefined
- 赋值语句返回等号后面部分,调用为独立调用
做一做
- 数组用索引获取后直接调用(引用类型调用,指向这个引用类型)
let length = 10
function fn() {
console.error(this.length)
}
const arr = [fn, 10, 20]
arr[0]()
const fn2 = arr[0]
fn2()
let声明的变量不会成为window的属性 window.length = 0
- 箭头函数的this,看所在上下文中的this
const obj = {
name: 'obj',
showThis: function() {
console.log(this);
}
}
obj.showThis()
const obj1 = {
name: 'obj1',
showName: function() {
console.log('111', this)
let innerFunc = () => {
console.log('222', this.name);
};
innerFunc();
}
};
obj1.showName();
- 3.1
function showTime() {
console.log(new Date().getTime(), this);
}
var id = 1001;
var name = "宁王";
let jsonObj = {
id: 9527,
name: "华安",
showAge: function () {
console.log("年方18", this);
console.log(this.id, this.name);
}
};
showTime = jsonObj.showAge;
showTime();
3.2
function showTime() {
console.log(new Date().getTime(), this);
}
var id = 1001;
var name = "宁王";
let jsonObj = {
id: 9527,
name: "华安",
showAge: function () {
console.log("年方18", this);
console.log(this.id, this.name);
}
};
jsonObj.showAge = showTime;
jsonObj.showAge();
3.3 引用类型都可以作为调用者
var id = 1001;
var name = "宁王";
let jsonObj = {
id: 9527,
name: "华安",
tiger: {
id: 8866,
name: "江小白",
showAge: function () {
console.log(this);
console.log(this.id, this.name);
}
}
};
jsonObj.tiger.showAge();
function showTime1(X) {
console.log("showTime1+" + X, this);
}
function showTime2() {
this.showTime1(130);
}
showTime1(110);
window.showTime1(120);
showTime2();
var var_id = 110;
var var_fun = function (param) {
console.log("var_fun", this.var_id, param);
};
let let_fun = function () {
console.log("let_fun", this.var_id, this);
};
var_fun(1);
window.var_fun(2);
let_fun();
let 定义的函数作用域是 Script,并不会提升到顶层对象 window 下去,但是在定义位置后面都可以使用此函数,包括后面引入的script文件中也照样可以调用 let_fun不提升为window的属性,但是直接调用它,他内部的this还是window,可以这样理解,全局环境下this指向window
- new关键字将构造函数的this指向新建的对象
function Tiger() {
this.id = 9527;
this.name = "华安";
console.log("构造器执行", this.id);
this.showAge = function () {
console.log(this.name + " 今年 28 岁.");
}
}
let tiger = new Tiger();
console.log(tiger);
console.log(tiger.id, tiger.name);
tiger.showAge();
- call 7.1
var id = 120;
var name = "华太师";
let json = {
id: 110,
name: "华安"
};
function showTime() {
console.log(this);
console.log(this.id, this.name);
}
showTime();
showTime.call(json);
7.2
function fun() {
console.log(this.age);
}
var person = {
age: 20,
fun
}
var age = 28;
var fun = person.fun;
fun.call(person);
fun.apply(person);
fun.bind(person)();
fun.call(null);
传null指向window function声明的提升优先级高于 var声明的变量 var只提升声明,不提升赋值 如果同一作用域中存在同名的 function和 var,函数的声明会覆盖变量的声明
const showInfo2 = () => {
console.log(this);
}
var Tiger = function () {
this.id = 110;
this.name = "宁王";
/**Dog 是一个 Json对象*/
this.Dog = {
id: 120,
name: "华太师",
/**非箭头函数写法*/
showInfo1: function () {
console.log(this.id, this.name);
},
/**箭头函数写法*/
showInfo2: () => {
console.log(this);
}
}
};
/**这种写法已经说过了,调用 showInfo1 函数的是 Dog 对象,里面的 this 就表示 Dog 对象*/
new Tiger().Dog.showInfo1();
/**虽然调用 showInfo2 函数的是 Dog 对象,但是箭头函数会往外抛一层,里面的 this 表示的是 Tiger*/
new Tiger().Dog.showInfo2();
- 9.1
const obj ={name:'xiaoming'};
function fn(){
console.log(this);
return ()=>{
console.log(this);
}
}
fn.call(obj)()
fn()()
9.2 {}不构成单独的作用域
const obj = {
name: 'obj',
obj3: {
name: 'obj3',
foo: () => {
console.log(this)
}
}
}
obj.obj3.foo()
箭头函数没有自己的 this,它继承的是定义时所在作用域的 this(即“词法作用域”),不在乎谁调用它 foo定义在 obj3对象中,但对象字面量 {}不构成单独的作用域,因此 foo的外层作用域实际上是全局作用域(即 window或 global)。 9.3 同上,{}并不是一个作用域
const obj = {
name: 'obj',
obj3: {
name: 'obj3',
foo: function () {
console.error(this)
return {
name: 'obj4',
foo: () => {
console.log(this)
}
}
}
}
}
obj.obj3.foo().foo()
var btn = document.querySelector('button');
btn.onclick = function () {
console.log(this); // btn <button>点击</button>
}
setTimeout(function(){
console.log(this);
}, 2000); //this指向window
setTimeout(() => {
console.log(this); // 取决于外层 this(可能是 window 或某个对象)
}, 2000);
(function(){
console.log(this); //this指向window
})();
setTimeout的回调函数是被 JavaScript 引擎直接调用的,独立调用的 立即执行函数(IIFE)是独立调用的,没有绑定到任何对象。普通函数在独立调用时,this默认指向全局对象(window),在严格模式下,独立调用的函数 this是 undefined
- 赋值语句
function foo() {
console.error(this)
}
const obj1 = {
name: 'obj1',
foo
}
const obj2 = {
name: 'obj2'
}
console.log((obj2.foo = obj1.foo));
(obj2.foo = obj1.foo)()
赋值语句会把右边的结果返回,调用就是独立调用,所以是window
- 13.1
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
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
13.2
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隐式绑定
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
13.3
var name = 'window'
function Person(name) {
this.name = name
this.obj = {
name: 'obj1',
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()() // obj1
person1.obj.foo2.call(person2)() // person2
person1.obj.foo2().call(person2) // obj1