this含义
执行上下文有且只有三类,全局执行上下文,函数上下文,与eval上下文;由于eval一般不会使用,这里不做讨论
全局执行上下文只有一个,在客户端中一般由浏览器创建,也就是我们熟知的window对象,
我们能通过this直接访问到它。
同时window对象还是var声明的全局变量的载体。我们通过var创建的全局对象,都可以通过window直接访问
函数执行上下文可存在无数个,每当一个函数被调用时都会创建一个函数上下文;需要注意的是,同一个函数被多次调用,都会创建一个新的上下文。
执行上下文栈(下文简称执行栈)也叫调用栈,执行栈用于存储代码执行期间创建的所有上下文,具有LIFO(Last In First Out后进先出,也就是先进后出)的特性。
JS代码首次运行,都会先创建一个全局执行上下文并压入到执行栈中,之后每当有函数被调用,都会创建一个新的函数执行上下文并压入栈内;由于执行栈LIFO的特性,所以可以理解为,JS代码执行完毕前在执行栈底部永远有个全局执行上下文。
1.全局执行上下文一般由浏览器创建,代码执行时就会创建;函数执行上下文只有函数被调用时才会创建,调用多少次函数就会创建多少上下文。
2.调用栈用于存放所有执行上下文,满足FILO规则。
3.执行上下文创建阶段分为绑定this,创建词法环境,变量环境三步,两者区别在于词法环境存放函数声明与const let声明的变量,而变量环境只存储var声明的变量。
4.词法环境主要由环境记录与外部环境引入记录两个部分组成,全局上下文与函数上下文的外部环境引入记录不一样,全局为null,函数为全局环境或者其它函数环境。环境记录也不一样,全局叫对象环境记录,函数叫声明性环境记录。
5.你应该明白了为什么会存在变量提升,函数提升,而let const没有。
6.ES3之前的变量对象与活动对象的概念在ES5之后由词法环境,变量环境来解释,两者概念不冲突,后者理解更为通俗易懂。
1.在全局环境中的this——window
"use strict"
console.log(this); //window
console.log(this===window);//true
2.在函数内部,this的值取决于函数被调用的方式。
函数内部中的 this 指向谁,不是在函数定义时决定的,而是在函数第一次调用并
执行的时候决定的
<script>
function mycall(){
console.log(54,this) //window
}
mycall()
</script>
2.1在函数中的this——window
在函数内部,this的值取决于函数被调用的方式。
在函数中的this——window
function f() {
console.log(this); //window
console.log(this===window);//true
}
f()
console.log(f()===window.f()); //true
2.2函数在严格模式下——undefined
function f() {
"use strict"
console.log(this); //undefined
console.log(this === window); //false
}
f()
3.对象中的this——指向调用者
function fun() {
console.log(this.name);
}
let obj = {
name: 'objName',
fn: fun
}
var name = "windowName"
obj.fn() //objName
fun() //windowName
function foo() {
console.log(this.a);
}
var obj2 = {
a: 2,
fn: foo
}
var obj1 = {
a: 1,
o1: obj2
}
obj1.o1.fn() //2
4.构造函数中的this——当前实例化对象
var obj = new a();
通过这条语句新创建了一个对象,把函数中的this指向了obj。
function Animal(name) {
this.type="动物"
this.name = name
this.say = function () {
console.log(this);
}
}
var cat = new Animal('Cat')
cat.say() //{name:'Cat',type:'动物',say:f()}
5.定时器中的this——window
II.改变this方法
A.bind方法
bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind()
的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
function.bind(thisArg[, arg1[, arg2[, ...]]])
const module = {
x: 42,
getX: function () {
return this.x;
}
};
const unboundGetX = module.getX;
console.log(unboundGetX());//unboundGetX普通函数this指向windonw 环境不存在x undefined
const boundGetX = unboundGetX.bind(module); //把unboundGetX的this指向module
console.log(boundGetX()); //42
B.call方法
call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。
function.call(thisArg, arg1, arg2, ...)
var person = {
fullName: function (city, country) {
console.log( this.firstName + " " + this.lastName + "," + city + "," + country)
return this.firstName + " " + this.lastName + "," + city + "," + country;
}
}
var person1 = {
firstName: "Bill",
lastName: "Gates"
}
person.fullName.call(person1);//Bill Gates,undefined,undefined
person.fullName.call(person1, "Seattle", "USA");//Bill Gates,Seattle,USA
B.1 手写call
/**
* 首先需要处理this指向(先临时存储指向当前函数的this)
* 再需要考虑数据类型的关系
* 执行函数
* 清除绑定关系this
*/
Function.prototype.callFun = function (_argObj, ...args) {
const _that = this;
// 判断_argObj是否存在并且是否属于Object
_argObj = (_argObj && _argObj instanceof Object) ? _argObj : window;
_argObj.fun = _that;
// 执行_argObj下的fun函数
_argObj.fun(...args);
// 执行完再清除_argObj下的fun属性
delete _argObj.fun;
}
function bar() {
console.log('bar_this', this)
console.log('bar(): ', ...arguments); // 打印参数
}
var obj = {
name: 'cky',
age: 24
}
bar.callFun(obj, obj.name, obj.age);
C.apply方法
apply() 方法调用一个具有给定 this 值的函数,以及以一个数组(或类数组对象)
的形式提供的参数。
func.apply(thisArg, [argsArray])
var person = {
fullName: function (city, country) {
console.log( this.firstName + " " + this.lastName + "," + city + "," + country)
return this.firstName + " " + this.lastName + "," + city + "," + country;
}
}
var person1 = {
firstName: "Bill",
lastName: "Gates"
}
person.fullName.apply(person1);//Bill Gates,undefined,undefined
person.fullName.apply(person1, ["深圳", "中国"]);//Bill Gates,深圳,中国
C.1手写apply
D.call和apply区别
方法定义:
apply:调用一个对象的一个方法,用另一个对象替换当前对象。
例如:B.apply(A, arguments);即A对象应用B对象的方法。
call:调用一个对象的一个方法,用另一个对象替换当前对象。
例如:B.call(A, args1,args2);在JavaScript中,call和apply作用是一样的,
都是为了改变某个函数运行时的上下文(context)而存在的,换句话说,
就是为了改变函数体内部this的指向即A对象调用B对象的方法。
call 与 apply 的相同点:
方法的含义是一样的,即方法功能是一样的;
第一个参数的作用是一样的;
call 与 apply 的不同点:两者传入的列表形式不一样
call可以传入多个参数;
apply只能传入两个参数,所以其第二个参数往往是作为数组形式传入
传递参数的方式。用法上不同,主要是参数不完全同
注意call方法中的参数arg:
a) arg的个数为零或多个;
b) arg可以是任何类型,包括Array。
call在这里译为“调用”,在JS中可以理解为“A对象调用B对象的某个方法”;
注意apply方法中的参数args:
a) args是Array对象的一个实例,也就是一个数组;
b) args的个数为零(就是说不起作用)或1个;。
apply在这里译为“应用”,即“A对象应用B对象的某个方法”。
E.总结
apply 、 call 、bind 三者都是用来改变函数的this对象的指向的;
apply 、 call 、bind 三者第一个参数都是this要指向的对象,也就是想指定的上下文;
apply 、 call 、bind 三者都可以利用后续参数传参;
bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用