JavaScript中函数调用有如下4种方式:
1.作为一个函数直接被调用, func()。
此时,在非严格模式中函数上下文this指向全局window;在严格模式中指向undefined
2.作为一个对象的方法调用,obj.func()。
此时,上下文this指向这个调用它的对象obj
3.作为构造函数调用new Func(),实例化一个新的对象。
此时,构造函数内部的this指向其实例化的对象。
function Func(name) {
this.name = name
}
let obj1 = new Func('zhangshan');
let obj2 = new Func('lisi');
obj1.name // => 'zhangshan'
obj2.name // => 'lisi'
当通过new关键字调用构造函数时会做如下操作:
1).创建一个新的空对象。
2).该对象作为this参数传递给构造函数,从而成为构造函数的函数上下文(构造函数的作用域赋给新对象,因此构造函数中 的 this指向了这个新对象)。
3).以创建的对象为上下文执行构造函数中的代码(为新对象添加属性)
4).新构造的对象作为new运算符的返回值
注:
a:如果构造函数返回一个对象,则该对象将作为整个表达式的值返回, 而传入构造函数的this将被丢弃。
b.如果构造函数返回的是非对象类型,则忽略返回值,返回新创建的对象。
c.每个函数都有一个原型对象(可以通过函数的prototype属性访问),该原型对象将被自动设置为通过该函数创建对象的原型。函数的原型可以被任意替换,已经构建的实例引用旧的原型,重新定义函数原型对已经构建的实例没有影响。
//new 操作过程可以用如下代码来表示
let obj = new Object();
Constructor = [].shift.call(arguments);
obj.__proto__ = Constructor.prototype;
let ret = Contructor.apply(obj, arguments);
return typeof ret === 'object' ? ret : obj
function Func(name) {
this.name = name
return 1
}
let obj = new Func('zhangshan');
obj // => 忽略构造函数中返回值1 还是返回对象 { name: 'zhangshan'};
function Func(name) {
this.name = name
return {
name: 'wang2'
}
}
let obj2 = new Func('lisi);
obj2 // => { name: 'wang2'}
4.通过函数的apply或者call方法——func.apply(obj)或者func.call(obj)。
let obj1 = {
name: 'zhangshan'
}
let obj2 = {
name: 'lisi'
}
function func(age) {
console.log(`${this.name} age ${age}`)
}
func.call(obj1, 22) // => zhangshan age 22
func.call(obj2, 26) // => lisi age 26
//call, apply第一个参数是函数的上下文this
//如果第一个参数是null,在非严格模式下this指向window, 严格模式下指向null
//apply和call的用法是一样的除了call传递的参数必须一个一个列出来,apply是以数组形式传递参数
在JavaScript中this的值会根据函数的调用方式不同而不同,有时候会使程序中this出现与预期不一致的情况。
决解方式有两种:使用es6的箭头函数 和 通过bind绑定上下文
function Ninja(){
this.whoAmI = () => this;
}
var ninja1 = new Ninja();
var ninja2 = {
whoAmI: ninja1.whoAmI
};
ninja1.whoAmI() === ninja1 //true
ninja2.whoAmI() === ninja2 //false
//在箭头函数中this等于函数定义时的上下文
//在这个例子中this是指向ninja1
//所以ninja2.whoAmI() 还是指向ninja1
function Ninja(){
this.whoAmI = function(){
return this;
}.bind(this);
}
var ninja1 = new Ninja();
var ninja2 = {
whoAmI: ninja1.whoAmI
};
ninja1.whoAmI() === ninja1 // true
ninja2.whoAmI() === ninja2 //false
//在实例化ninja1的时候, ninja1.whoAmI中的this被绑定为ninja1
//所以不管怎样的方式调用ninja1.whoAmI都是指向 ninja1
//注:此例摘录自 《JavaSript忍者秘籍第2版》
const obj = {
func1() {
return this === obj
},
func2: () => this === window
}
console.log(obj.func1()) //true
console.log(obj.func2()) //true