9.2this

this

介绍:JavaScript 语言之所以有this的设计,跟内存里面的数据结构有关系。

var obj = { foo:  5 };

上面的代码将一个对象赋值给变量obj。JavaScript 引擎会先在内存里面,生成一个对象{ foo: 5 },然后把这个对象的内存地址赋值给变量obj。

也就是说,变量obj是一个地址。后面如果要读取obj.foo,引擎先从obj拿到内存地址,然后再从该地址读出原始的对象,返回它的foo属性。

概念:原始的对象以字典结构保存,每一个属性名都对应一个属性描述对象。举例来说,上面例子的foo属性,实际上是以下面的形式保存的。

{
  foo: {
    [[value]]: 5
    [[writable]]: true
    [[enumerable]]: true
    [[configurable]]: true
  }
}

注意:foo属性的值保存在属性描述对象的value属性里面。

这样的结构是很清晰的,问题在于属性的值可能是一个函数。

var obj = { foo: function () {} };

这时,引擎会将函数单独保存在内存中,然后再将函数的地址赋值给foo属性的value属性。

{
  foo: {
    [[value]]: 函数的地址
    ...
  }
}

由于函数也是一个对象是一个单独的值,所以它可以在不同的环境(上下文)执行。

var f = function () {};
var obj = { f: f };

// 单独执行
f()

// obj 环境执行
obj.f()

思考:JavaScript 允许在函数体内部,引用当前环境的其他变量。下面代码中,函数体里面使用了变量x。该变量由运行环境提供。那么,由于函数可以在不同的运行环境执行,所以需要有一种机制,能够在函数体内部获得当前的运行上下文环境。所以,this就出现了,它的设计目的就是在函数体内部,指代函数当前的运行环境。

var f = function () {
  console.log(x);
};

下面代码中,函数体里面的this.x就是指当前运行环境的x。

var f = function () {
  console.log(this.x);
}

例子:下面代码中,函数f在全局环境执行,this.x指向全局环境的x。在obj环境执行,this.x指向obj.x。

var f = function () {
  console.log(this.x);
}

var x = 1;
var obj = {
  f: f,
  x: 2,
};

// 单独执行
f() // 1

// obj 环境执行
obj.f() // 2

总结:我们可以这样理解,因为obj.foo()是通过obj找到foo,所以就是在obj环境执行。一旦var foo = obj.foo,变量foo就直接指向函数本身,所以foo()就变成在全局环境执行。

this的指向问题

概念:在函数中除了声明时定义的形式参数,每个函数还接受2个附加的参数:this 和 argument。其中 this 在面向对象编程中非常重要,他的值取决于调用的模式共四种:

  1. 方法调用模式
  2. 函数调用模式
  3. 构造器调用模式
  4. apply调用模式

以上四种调用模式在如何初始化关键参数this上存在差异

方法调用模式

介绍:当一个函数被保存微对象的一个属性时,我们称其为一个方法。当一个方法被调用时,this被绑定到该对象。如果调用表达式包含一个提取属性的动作(即包含一个.表达式或[subscript]下标表达式,那么他就是被当做一个方法来调用)

此时方法可以使用this访问自己所属的对象,所以他能从对象中取值对对象进行修改

(this到对象的绑定发生在调用的时候,此特性可以使得函数可以对this高度复用。通过this可取得他们所属对象的上下文的方法称为公共方法)

var mayObj = {
    value: 0,
    increment: function(inc) {
        this.value += typeof inc === 'number'? inc : 1
    }
}

myObj.increment()
console.log(myObj.value) // 1

myObj.increment(2)
console.log(myObj.value) // 3
函数调用模式

介绍:当一个函数并未一个对象的属性时,那么他就是被当做一个函数来调用的,以此模式调用函数时。this会被绑定到全局对象

注意:这种this的绑定是语言设计上的错误,因为如果语言设计正确,那么当函数被调用时,this应该仍然绑定到外部函数的this变量,这个设计的错误后果就是方法不能利用内部函数来帮助他工作,因为内部函数的this被绑定的错误的值,所以不能共享该方法对对象的访问权

构造器调用模式

介绍:javascript 是一门基于原型继承的语言,所以对象可以直接从其他对象继承属性,单当今大多数语言都是基于的语言。虽然原型继承极富表现力,但他并未被广泛理解,所以JavaScript提供了一套基于类的语言的构建语法。如果在一个函数前面加上 new 关键字来调用,那么函数将会连接到prototype 成员的新对象,同时this会被绑定到这个新对象上

一个函数如果创建的目的就是希望结合new 前缀来调用,那他就被称为构造器函数。按照约定,他们保存在以大写格式命名的变量里。如果调用构造器函数没有在前面加上new,可能会发生非常糟糕的事情。

Apply 调用模式

因为JavaScript是一门函数式的面向对象编程语言,所以函数可以拥有方法,apply方法让我们构建一个参数数组传递给调用函数,它允许我们选择this的值,apply方法接受两个参数。第一个是要绑定给this的值,第二个就是一个参数数组


apply

介绍:apply() 方法调用一个具有给定this值的函数,以及以一个数组(或类数组对象)的形式提供的参数。

语法

func.apply(thisArg, [argsArray])
  • 参数
    • thisArg: 在 func 函数运行时使用的 this 值。

    请注意,this可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动替换为指向全局对象。

  • argsArray:一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数。如果该参数的值为 null 或 undefined,则表示不需要传入任何参数。
  • 返回值 指定this值和参数后的func函数调用后的返回值。

例子

function Product(name, price) {
  this.name = name;
  this.price = price;
}

function Food(name, price) {
  Product.apply(this, [name, price]);
  this.category = 'food';
}

console.log(new Food('cheese', 5).name);

call

介绍:call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。

注意:该方法的语法和作用与 apply() 方法类似,只有一个区别,就是 call() 方法接受的是一个参数列表,而 apply() 方法接受的是一个包含多个参数的数组。

语法:

function.call(thisArg, arg1, arg2, ...)
  • 参数
    • thisArg 在 function 函数运行时使用的 this 值。
    • arg1, arg2, … 指定的参数列表。
  • 返回值 指定this值和参数后的func函数调用后的返回值。

例子

function Product(name, price) {
  this.name = name;
  this.price = price;
}

function Food(name, price) {
  Product.call(this, name, price);
  this.category = 'food';
}

console.log(new Food('cheese', 5).name);

bind

介绍:bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

语法

function.bind(thisArg[, arg1[, arg2[, ...]]])
  • 参数

    • thisArg 调用绑定函数时作为 this 参数传递给目标函数的值。 如果使用new运算符构造绑定函数,则忽略该值。如果thisArg是null或undefined,执行作用域的 this 将被视为新函数的 thisArg。
    • arg1, arg2, … 当目标函数被调用时,被预置入绑定函数的参数列表中的参数。
  • 返回值返回一个原函数的拷贝,并拥有指定的 this 值和初始参数。

例子

const module = {
  x: 42,
  getX: function() {
    return this.x;
  }
};

const unboundGetX = module.getX;
console.log(unboundGetX()); // 该函数在全局作用域被调用
// undefined

const boundGetX = unboundGetX.bind(module);
console.log(boundGetX());
// 42

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值