Javascript中的关键字'this'学习笔记

目标

  • 定义什么是this关键字
  • 理解四种常用的方法去找出关键字this代表什么
  • 尽可能的在语句中不要使用this

'this' 是什么

  • JavaScript中的保留关键字
  • 通常由函数的调用方式决定(我们称之为执行上下文)
  • 可以用四种规则来确定(全局(默认绑定), 对象/隐式, 显式, new)

1. 全局环境/默认绑定(global context)

  1. 当“this”不在已声明对象的内部时, this 会指向window
  console.log(this)  // Window 
复制代码
   function whatIsThis () {
     return this;
   }
   whatIsThis()
复制代码
  function variablesInThis(){this.person = 'elie'}
  variablesInThis() //this ---> window
  console.log(person) //  elie
复制代码
  1. 严格模式下面
   "use strict"

    console.log(this); // window

    function whatIsThis(){
        return this;
    }

    whatIsThis(); // undefined

    function variablesInThis(){
        this.person = "Elie";
    }
    variablesInThis(); // TypeError, can't set person on undefined! 
复制代码

2. 隐式绑定/对象 (Implicit/Object)

  1. 当“this”在已声明对象的内部时, this会被绑定到对象,但是嵌套对象中this会丢失! 会丢失!
  var person = {
    firstName:'Elie',
    sayHi: function() {
      return "Hi" + this.firstName
    },
    determineContext: function() {
      return this === person
    }
  }

  // 测试
  person.sayHi() // "Hi Elie"
  person.determineContext(); // true
复制代码

思考 这里的关键字“this”应该指什么?

  var person = {
    firstName: "张三",
    determineContext: this
  }
  person.determineContext; // window
复制代码

函数运行时定义一个关键字“this”!这里没有运行一个函数来创建关键字'this'的新值,所以'this'的值仍然是窗口!

  1. 嵌套对象 (Nested Objects) 当我们有一个嵌套对象的时候,this绑定会如何
  var person = {
    firstName: '张三',
    sayHi: function () {
      return 'Hi' + this.firstName
    },
    determineContext: function() {
      return this === person
    },
    dog: {
      sayHello: function() {
        return 'Hello' + this.firstName
      },
      determineContext: function() {
        return this === person
      }
    }
  }
person.sayHi(); // "Hi张三"
person.determineContext(); // true

// 嵌套对象中的this 
person.dog.sayHello(); // "Helloundefined"
person.dog.determineContext(); // false
复制代码

嵌套对象中的this 丢失了!!! 丢失了!!!


3. 显式绑定 (Explicit Binding)

显式绑定一般通过使用 call apply 和 bind ,三种方法的区别

  方法名              参数                    立即调用?
  Call      thisArg, a, b, c, d , ...           YES
  Apply     thisArg, [a,b,c,d, ...]             YES
  Bind      thisArg, a, b, c, d , ...           NO 
复制代码
  1. 显式绑定call 修复了嵌套对象丢失this问题
  var person = {
    firstName: '张三',
    sayHi: function () {
      return 'Hi' + this.firstName
    },
    determineContext: function() {
      return this === person
    },
    dog: {
      sayHello: function() {
        return 'Hello' + this.firstName
      },
      determineContext: function() {
        return this === person
      }
    }
  }
  // 测试
  person.dog.sayHello.call(person); // "Hello张三"
  person.dog.determineContext.call(person); // true
复制代码

一个普通的例子

var javabook = {
  name:'java book',
  showBook: function() {
    return 'this book"s name is ' + this.name
  }
}
var phpbook = {
  name:'php book',
  showBook: function() {
    return 'this book"s name is ' + this.name
  }
}
javabook.showBook()  // "this book"s name is php book"
phpbook.showBook() //  "this book"s name is php book"

// 通过call 我们可以让showBook() 成为大家的
function showBook()  {
  return 'this book"s name is ' + this.name
}
var cssbook = {
  name:'css book'
}
var htmlbook = {
  name: 'html book'
}
//  test
showBook.call(cssbook)
// "this book"s name is css book"
showBook.call(htmlbook)
// "this book"s name is html book"
复制代码

再来一个call的例子

  1. 假设我们要选择页面上所有的“divs”
  var divs = document.getElementsByTagName('divs')  //类数组对象
复制代码
  1. 然后我们想找到含有"hello"的div, 我们想使用filter
divs.filter // undefined  它是一个类似对象的数组,所以过滤器不会起作用。
复制代码
  1. 解决办法 使用数组slice方法

Array.prototype.slice.call(arguments),你也可以简单的使用 [].slice.call(arguments) 来代替 slice 不修改原数组,只会返回一个浅复制了原数组中的元素的一个新数组。原数组的元素会按照下述规则拷贝:

  • 如果该元素是个对象引用 (不是实际的对象),slice 会拷贝这个对象引用到新的数组里。两个对象引用都引用了同一个对象。如果被引用的对象发生改变,则新的和原来的数组中的这个元素也会发生改变。
  • 对于字符串、数字及布尔值来说(不是 String、Number 或者 Boolean 对象),slice 会拷贝这些值到新的数组里。在别的数组里修改这些字符串或数字或是布尔值,将不会影响另一个数组。

var divsArray = [].slice.call(divs);

divsArray.filter(function(val){
    return val.innerText === 'Hello';
})
复制代码
  1. Apply? 看代码
function showBook()  {
  return 'this book"s name is ' + this.name
}
var cssbook = {
  name:'css book'
}
var htmlbook = {
  name: 'html book'
}
//  test
showBook.call(cssbook)  // "this book"s name is css book"
showBook.apply(cssbook) // "this book"s name is css book"
// 这样用看起来好像没有区别
复制代码

似乎相同的…但是如果我们开始添加参数会发生什么呢?

  function addNumbers(a,b,c,d){
    return this.name + "计算: " + (a+b+c+d);
  }
  var cssbook = {
  name:'css book'
  }
  var htmlbook = {
    name: 'html book'
  }

  addNumbers.call(cssbook,1,1,1,1)
  // "css book计算: 4"
  addNumbers.apply(cssbook,1,1,1,1)
  // VM273:1 Uncaught TypeError: CreateListFromArrayLike called on non-object
  //     at <anonymous>:1:12
  addNumbers.apply(cssbook,[1,1,1,1])
  // "css book计算: 4"
复制代码

所以 我们什么时候使用apply呢 当一个函数不接受一个数组时,apply将为我们在一个数组中展开值!

  var nums = [5,7,1,4,2]
  Math.max(nums)  // NaN
  Math.max.apply(this, nums) //7
复制代码
function sumValues(a,b,c){
    return a+b+c;
}

var values = [4,1,2];

sumValues(values); // "4,1,2undefinedundefined"
sumValues.apply(this,[4,1,2]); // 7
复制代码
  1. 再来看看bind

参数的工作方式类似于call,但是bind返回一个具有“this”绑定上下文的函数! 不会立刻执行需要调用

  function addNumbers(a,b,c,d){
      return this.name + " just calculated " + (a+b+c+d);
  }

  var htmlbook = {
    name: 'html book'
  }
  // test
  var htmlbookCalc = addNumbers.bind(htmlbook, 1,2,3,4)  // f(){}
  htmlbookCalc() // "html book just calculated 10" 
复制代码

bind的使用

在我们不想马上执行的函数中!,通常我们会失去“this”的上下文

var colt = {
    firstName: "Colt",
    sayHi: function(){
        setTimeout(function(){
            console.log("Hi " + this.firstName);
        },1000);
    }
}
// test 
colt.sayHi(); // Hi undefined (1000 milliseconds later)
复制代码

丢掉了 以前的解决方法 利用作用域保存this

var colt = {
    firstName: "Colt",
    sayHi: function(){
      var that = this
        setTimeout(function(){
            console.log("Hi " + that.firstName);
        },1000);
    }
}
// test 
colt.sayHi(); // Hi Colt 
复制代码

使用bind来设置“this”的正确上下文

var colt = {
    firstName: "Colt",
    sayHi: function(){
        setTimeout(function(){
            console.log("Hi " + this.firstName);
        }.bind(this),1000);
    }
}
colt.sayHi(); // Hi Colt 
复制代码

4. new绑定

我们可以使用“new”关键字来设置关键字“this”的上下文

function Person(firstName, lastName){
    this.firstName = firstName;
    this.lastName = lastName;
}
// test
var Lily = new Person("Lily", "Study")
Lily.firstName; // "Lily"
Lily.lastName; //  "Study"
复制代码

总结

  1. 关键字“this”是JavaScript中的保留关键字,其值在执行时确定
  2. 它可以使用全局上下文、对象绑定、显式绑定或new关键字来设置
  3. 当在函数的全局上下文中设置时,它要么是全局对象(在浏览器中是窗口),要么是未定义的对象(如果我们使用严格模式)
  4. 要显式地设置关键字“this”的值,我们使用call、apply或bind
  5. 我们还可以使用“new”关键字来设置“this”的上下文
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值