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

  1. "use strict"
  2. console.log(this); //window
  3. console.log(this===window);//true

2.在函数内部,this的值取决于函数被调用的方式。

  1. 函数内部中的 this 指向谁,不是在函数定义时决定的,而是在函数第一次调用并
  2. 执行的时候决定的
  3. <script>
  4. function mycall(){
  5. console.log(54,this) //window
  6. }
  7. mycall()
  8. </script>

2.1在函数中的this——window

  1. 在函数内部,this的值取决于函数被调用的方式。
  2. 在函数中的this——window
  3. function f() {
  4. console.log(this); //window
  5. console.log(this===window);//true
  6. }
  7. f()
  8. console.log(f()===window.f()); //true

2.2函数在严格模式下——undefined

  1. function f() {
  2. "use strict"
  3. console.log(this); //undefined
  4. console.log(this === window); //false
  5. }
  6. f()

3.对象中的this——指向调用者

  1. function fun() {
  2. console.log(this.name);
  3. }
  4. let obj = {
  5. name: 'objName',
  6. fn: fun
  7. }
  8. var name = "windowName"
  9. obj.fn() //objName
  10. fun() //windowName
  11. function foo() {
  12. console.log(this.a);
  13. }
  14. var obj2 = {
  15. a: 2,
  16. fn: foo
  17. }
  18. var obj1 = {
  19. a: 1,
  20. o1: obj2
  21. }
  22. obj1.o1.fn() //2

4.构造函数中的this——当前实例化对象

  1. var obj = new a();
  2. 通过这条语句新创建了一个对象,把函数中的this指向了obj
  1. function Animal(name) {
  2. this.type="动物"
  3. this.name = name
  4. this.say = function () {
  5. console.log(this);
  6. }
  7. }
  8. var cat = new Animal('Cat')
  9. cat.say() //{name:'Cat',type:'动物',say:f()}

5.定时器中的this——window

II.改变this方法

A.bind方法

  1. bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind()
  2. 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
  3. function.bind(thisArg[, arg1[, arg2[, ...]]])
  4. const module = {
  5. x: 42,
  6. getX: function () {
  7. return this.x;
  8. }
  9. };
  10. const unboundGetX = module.getX;
  11. console.log(unboundGetX());//unboundGetX普通函数this指向windonw 环境不存在x undefined
  12. const boundGetX = unboundGetX.bind(module); //把unboundGetX的this指向module
  13. console.log(boundGetX()); //42

B.call方法

  1. call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。
  2. function.call(thisArg, arg1, arg2, ...)
  3. var person = {
  4. fullName: function (city, country) {
  5. console.log( this.firstName + " " + this.lastName + "," + city + "," + country)
  6. return this.firstName + " " + this.lastName + "," + city + "," + country;
  7. }
  8. }
  9. var person1 = {
  10. firstName: "Bill",
  11. lastName: "Gates"
  12. }
  13. person.fullName.call(person1);//Bill Gates,undefined,undefined
  14. person.fullName.call(person1, "Seattle", "USA");//Bill Gates,Seattle,USA

B.1 手写call

  1. /**
  2. * 首先需要处理this指向(先临时存储指向当前函数的this)
  3. * 再需要考虑数据类型的关系
  4. * 执行函数
  5. * 清除绑定关系this
  6. */
  7. Function.prototype.callFun = function (_argObj, ...args) {
  8. const _that = this;
  9. // 判断_argObj是否存在并且是否属于Object
  10. _argObj = (_argObj && _argObj instanceof Object) ? _argObj : window;
  11. _argObj.fun = _that;
  12. // 执行_argObj下的fun函数
  13. _argObj.fun(...args);
  14. // 执行完再清除_argObj下的fun属性
  15. delete _argObj.fun;
  16. }
  17. function bar() {
  18. console.log('bar_this', this)
  19. console.log('bar(): ', ...arguments); // 打印参数
  20. }
  21. var obj = {
  22. name: 'cky',
  23. age: 24
  24. }
  25. bar.callFun(obj, obj.name, obj.age);

C.apply方法

  1. apply() 方法调用一个具有给定 this 值的函数,以及以一个数组(或类数组对象)
  2. 的形式提供的参数。
  3. func.apply(thisArg, [argsArray])
  4. var person = {
  5. fullName: function (city, country) {
  6. console.log( this.firstName + " " + this.lastName + "," + city + "," + country)
  7. return this.firstName + " " + this.lastName + "," + city + "," + country;
  8. }
  9. }
  10. var person1 = {
  11. firstName: "Bill",
  12. lastName: "Gates"
  13. }
  14. person.fullName.apply(person1);//Bill Gates,undefined,undefined
  15. person.fullName.apply(person1, ["深圳", "中国"]);//Bill Gates,深圳,中国

C.1手写apply

D.call和apply区别

  1. 方法定义:
  2. apply:调用一个对象的一个方法,用另一个对象替换当前对象。
  3. 例如:B.apply(A, arguments);即A对象应用B对象的方法。
  4. call:调用一个对象的一个方法,用另一个对象替换当前对象。
  5. 例如:B.call(A, args1,args2);在JavaScript中,callapply作用是一样的,
  6. 都是为了改变某个函数运行时的上下文(context)而存在的,换句话说,
  7. 就是为了改变函数体内部this的指向即A对象调用B对象的方法。
  1. call apply 的相同点:
  2. 方法的含义是一样的,即方法功能是一样的;
  3. 第一个参数的作用是一样的;
  4. call apply 的不同点:两者传入的列表形式不一样
  5. call可以传入多个参数;
  6. apply只能传入两个参数,所以其第二个参数往往是作为数组形式传入
  1. 传递参数的方式。用法上不同,主要是参数不完全同
  2. 注意call方法中的参数arg:
  3. a) arg的个数为零或多个;
  4. b) arg可以是任何类型,包括Array
  5. call在这里译为“调用”,在JS中可以理解为“A对象调用B对象的某个方法”;
  6. 注意apply方法中的参数args:
  7. a) argsArray对象的一个实例,也就是一个数组;
  8. b) args的个数为零(就是说不起作用)或1个;。
  9. apply在这里译为“应用”,即“A对象应用B对象的某个方法”。

E.总结

  1. apply call bind 三者都是用来改变函数的this对象的指向的;
  2. apply call bind 三者第一个参数都是this要指向的对象,也就是想指定的上下文;
  3. apply call bind 三者都可以利用后续参数传参;
  4. bind 是返回对应函数,便于稍后调用;apply call 则是立即调用