默认绑定
默认绑定通常是指函数独立调用,不涉及其他绑定规则。非严格模式下,this指向window,严格模式下,this指向undefined。
非严格模式
var foo = 123;
function print(){
this.foo = 234;
console.log(this); // window
console.log(foo); // 234
}
print();
严格模式
"use strict";
var foo = 123;
function print(){
console.log('print this is ', this); //undefined
console.log(window.foo)
console.log(this.foo);
}
console.log('global this is ', this); //window
print();
let/const
let/const定义的变量存在暂时性死区,而且不会挂载到window对象上,因此print中是无法获取到a和b。
let a = 1;
const b = 2;
var c = 3;
function print() {
console.log(this.a); //undefined
console.log(this.b); //undefined
console.log(this.c); //3
}
print();
console.log(this.a); //undefined
对象内执行
a = 1;
function foo() {
console.log(this.a);
}
const obj = {
a: 10,
bar() {
foo(); // 1
}
}
obj.bar();
函数内执行
var a = 1
function outer () {
var a = 2
function inner () {
console.log(this.a) // 1
}
inner()
}
outer()
自执行函数
a = 1;
(function(){
console.log(this); //window
console.log(this.a) //1
}())
function bar() {
b = 2;
(function(){
console.log(this); //window
console.log(this.b) //2
}())
}
bar();
隐式绑定
函数的调用是在某个对象上触发的,即调用位置存在上下文对象,通俗点说就是**XXX.func()**这种调用模式。
此时func的this指向XXX,但如果存在链式调用,例如XXX.YYY.ZZZ.func,记住一个原则:this永远指向最后调用它的那个对象。
隐式绑定
var a = 1;
function foo() {
console.log(this.a);
}
// 对象简写,等同于 {a:2, foo: foo}
var obj = {a: 2, foo}
foo(); //1
obj.foo(); //2
对象链式调用
var obj1 = {
a: 1,
obj2: {
a: 2,
foo(){
console.log(this.a)
}
}
}
obj1.obj2.foo() // 2
取函数别名
a = 1
var obj = {
a: 2,
foo() {
console.log(this.a)
}
}
var foo = obj.foo;
obj.foo(); //2
foo(); //1
不要把这里理解成
window.foo执行,如果foo为let/const定义,foo不会挂载到window上,但不会影响最后的打印结果
var obj = {
a: 1,
foo() {
console.log(this.a)
}
};
var a = 2;
var foo = obj.foo;
var obj2 = { a: 3, foo: obj.foo }
obj.foo(); //1
foo(); //2
obj2.foo(); /3
函数作为参数传递
function foo() {
console.log(this,this.a) //window,2
}
function doFoo(fn) {
console.log(this) //window
fn()
}
var obj = { a: 1, foo }
var a = 2
doFoo(obj.foo)
obj.foo作为实参,在预编译时将其值赋值给形参fn,是将obj.foo指向的地址赋给了fn,此后fn执行不会与obj产生任何关系。fn为默认绑定。
function foo() {
console.log(this,this.a) //window,2
}
function doFoo(fn) {
console.log(this) //{a: 3, doFoo: ƒ}
fn()
}
var obj = { a: 1, foo }
var a = 2
var obj2 = { a: 3, doFoo }
obj2.doFoo(obj.foo)
var length = 10
function fn () {
console.log(this.length)
}
var obj = {
length: 5,
method (fn) {
fn() //10
arguments[0]() //2
}
}
obj.method(fn, 1)
回调函数
var name='zcxiaobao';
function introduce(){
console.log('Hello,My name is ', this.name);
}
const Tom = {
name: 'TOM',
introduce: function(){
setTimeout(function(){
console.log(this)
console.log('Hello, My name is ',this.name);
})
}
}
const Mary = {
name: 'Mary',
introduce
}
const Lisa = {
name: 'Lisa',
introduce
}
Tom.introduce();
setTimeout(Mary.introduce, 100);
setTimeout(function(){
Lisa.introduce();
},200);
Window {…}
Hello, My name is zcxiaobao
Hello,My name is zcxiaobao
Hello,My name is Lisa
- Tom.introduce()执行: console位于setTimeout的回调函数中,回调函数的this指向window
- Mary.introduce直接作为setTimeout的函数参数,会发生隐式绑定丢失,this为默认绑定
- Lisa.introduce执行虽然位于setTimeout的回调函数中,但保持xxx.fn模式,this为隐式绑定。
const Tom = {
name: 'TOM',
introduce: function(){
_self = this
setTimeout(function(){
console.log('Hello, My name is ',_self.name); // Hello, My name is TOM
})
}
}
Tom.introduce()
隐式绑定丢失综合题
name = 'javascript' ;
let obj = {
name: 'obj',
A (){
this.name += 'this';
console.log(this.name)
},
B(f){
this.name += 'this';
f();
},
C(){
setTimeout(function(){
console.log(this.name);
},1000);
}
}
let a = obj.A;
a();
obj.B(function(){
console.log(this.name);
});
obj.C();
console.log(name);
javascriptthis
javascriptthis
javascriptthis
javascriptthis
显式绑定
显式绑定比较好理解,就是通过call()、apply()、bind()等方法,强行改变this指向。
比较三种调用方式
function foo () {
console.log(this.a)
}
var obj = { a: 1 }
var a = 2
foo() //2
foo.call(obj) //1
foo.apply(obj) //1
foo.bind(obj)
function foo () {
console.log(this.a)
}
var obj = { a: 1 }
var a = 2
foo()
foo.call(obj)
foo().call(obj)
foo().call(obj): 对foo()执行的返回值执行call,foo返回值为undefined,执行call()会报错
function foo () {
console.log(this.a)
return function() {
console.log(this.a)
}
}
var obj = { a: 1 }
var a = 2
foo()
foo.call(obj)
foo().call(obj)
2
1
2
1
外层this与内层this
function foo () {
console.log(this.a) //1
return function() {
console.log(this.a) //2
}
}
var obj = { a: 1 }
var a = 2
foo.call(obj)()
对象中的call
var obj = {
a: 'obj',
foo: function () {
console.log('foo:', this.a)
return function () {
console.log('inner:', this.a)
}
}
}
var a = 'window'
var obj2 = { a: 'obj2' }
obj.foo()()
obj.foo.call(obj2)()
obj.foo().call(obj2)
- obj.foo()(): 第一层obj.foo()执行为隐式绑定,打印出foo:obj;第二层匿名函数为默认绑定,打印inner:window
- obj.foo.call(obj2)(): 第一层obj.foo.call(obj2)使用call将obj.foo的this指向obj2,打印foo: obj2;第二层匿名函数默认绑定,打印inner:window
- obj.foo().call(obj2): 第一层隐式绑定,打印:foo: obj,第二层匿名函数使用call将this指向obj2,打印inner: obj2
显式绑定扩展
apply求数组最值
JavaScript中没有给数组提供类似max和min函数,只提供了Math.max/min,用于求多个数的最值,所以可以借助apply方法,直接传递数组给Math.max/min
const arr = [1,10,11,33,4,52,17]
Math.max.apply(Math, arr)
Math.min.apply(Math, arr)
类数组转为数组
ES6未发布之前,没有Array.from方法可以将类数组转为数组,采用Array.prototype.slice.call(arguments)或[].slice.call(arguments)将类数组转化为数组。
数组高阶函数
const obj = {a: 10}
const arr = [1, 2, 3, 4]
arr.forEach(function (val, key){
console.log(`${key}: ${val} --- ${this.a}`)
}, obj)
0: 1 --- 10
1: 2 --- 10
2: 3 --- 10
3: 4 --- 10
new绑定
使用new来构建函数,会执行如下四部操作:
- 创建一个空的简单
JavaScript对象(即{}); - 为步骤1新创建的对象添加属性
__proto__,将该属性链接至构造函数的原型对象 ; - 将步骤1新创建的对象作为
this的上下文 ; - 如果该函数没有返回对象,则返回
this。
属性加方法
function User (name, age) {
this.name = name;
this.age = age;
this.introduce = function () {
console.log(this.name)
}
this.howOld = function () {
return function () {
console.log(this.age)
}
}
}
var name = 'Tom';
var age = 18;
var zc = new User('zc', 24)
zc.introduce() //zc
zc.howOld()() //18
箭头函数
箭头函数没有自己的this,它的this指向外层作用域的this,且指向函数定义时的this而非执行时。
- this指向外层作用域的this: 箭头函数没有this绑定,但它可以通过作用域链查到外层作用域的this
- 指向函数定义时的this而非执行时: JavaScript是静态作用域,就是函数定义之后,作用域就定死了,跟它执行时的地方无关。
对象方法使用箭头函数
name = 'tom'
const obj = {
name: 'zc',
intro: () => {
console.log('My name is ' + this.name)
}
}
obj.intro() //My name is tom
箭头函数与普通函数比较
name = 'tom'
const obj = {
name: 'zc',
intro:function () {
return () => {
console.log('My name is ' + this.name)
}
},
intro2:function () {
return function() {
console.log('My name is ' + this.name)
}
}
}
obj.intro2()() //My name is tom
obj.intro()() //My name is zc
箭头函数与普通函数的嵌套
name = 'window'
const obj1 = {
name: 'obj1',
intro:function () {
console.log(this.name)
return () => {
console.log(this.name)
}
}
}
const obj2 = {
name: 'obj2',
intro: ()=> {
console.log(this.name)
return function() {
console.log(this.name)
}
}
}
const obj3 = {
name: 'obj3',
intro: ()=> {
console.log(this.name)
return () => {
console.log(this.name)
}
}
}
obj1.intro()() //obj1 obj1
obj2.intro()() //window window
obj3.intro()() //window window
new碰上箭头函数
function User(name, age) {
this.name = name;
this.age = age;
this.intro = function(){
console.log('My name is ' + this.name)
},
this.howOld = () => {
console.log('My age is ' + this.age)
}
}
var name = 'Tom', age = 18;
var zc = new User('zc', 24);
zc.intro();
zc.howOld();
- zc是new User实例,因此构造函数User的this指向zc
- zc.intro(): 打印My name is zc
- zc.howOld(): howOld为箭头函数,箭头函数this由外层作用域决定,且指向函数定义时的this,外层作用域为User,this指向zc,打印My age is 24
call碰上箭头函数
箭头函数由于没有this,不能通过call\apply\bind来修改this指向,但可以通过修改外层作用域的this来达成间接修改
var name = 'window'
var obj1 = {
name: 'obj1',
intro: function () {
console.log(this.name)
return () => {
console.log(this.name)
}
},
intro2: () => {
console.log(this.name)
return function () {
console.log(this.name)
}
}
}
var obj2 = {
name: 'obj2'
}
obj1.intro.call(obj2)()
obj1.intro().call(obj2)
obj1.intro2.call(obj2)()
obj1.intro2().call(obj2)
obj2
obj2
obj1
obj1
window
window
window
obj2
- obj1.intro.call(obj2)(): 第一层函数为普通函数,通过call修改this为obj2,打印obj2。第二层函数为箭头函数,它的this与外层this相同,同样打印obj2。
- obj1.intro().call(obj2): 第一层函数打印obj1,第二次函数为箭头函数,call无效,它的this与外层this相同,打印obj1
- obj1.intro2.call(obj2)(): 第一层为箭头函数,call无效,外层作用域为window,打印window;第二次为普通匿名函数,默认绑定,打印window
- obj1.intro2().call(obj2): 与上同,打印window;第二层为匿名函数,call修改this为obj2,打印obj2
- 箭头函数没有this,它的this是通过作用域链查到外层作用域的this,且指向函数定义时的this而非执行时。
- 不可以用作构造函数,不能使用new命令,否则会报错
- 箭头函数没有arguments对象,如果要用,使用rest参数代替
- 不可以使用yield命令,因此箭头函数不能用作Generator函数。
- 不能用call/apply/bind修改this指向,但可以通过修改外层作用域的this来间接修改。
- 箭头函数没有prototype属性。