2022年7月13日

对象方法,"this"

通常创建对象来表示真实世界中的实体,如用户和订单等:

let user = {
  name: "John",
  age: 30
};

并且,在现实世界中,用户可以进行 操作:从购物车中挑选某物、登录和注销等。

在 JavaScript 中,行为(action)由属性中的函数来表示。

方法示例

刚开始,我们来教 user 说 hello:

let user = {
  name: "John",
  age: 30
};

user.sayHi = function() {
  alert("Hello!");
};

user.sayHi(); // Hello!

这里我们使用函数表达式创建了一个函数,并将其指定给对象的 user.sayHi 属性。

随后我们像这样 user.sayHi() 调用它。用户现在可以说话了!

作为对象属性的函数被称为 方法

所以,在这我们得到了 user 对象的 sayHi 方法。

当然,我们也可以使用预先声明的函数作为方法,就像这样:

let user = {
  // ...
};

// 首先,声明函数
function sayHi() {
  alert("Hello!");
}

// 然后将其作为一个方法添加
user.sayHi = sayHi;

user.sayHi(); // Hello!
面向对象编程

当我们在代码中用对象表示实体时,就是所谓的 面向对象编程,简称为 “OOP”。

OOP 是一门大学问,本身就是一门有趣的科学。怎样选择合适的实体?如何组织它们之间的交互?这就是架构,有很多关于这方面的书,例如 E. Gamma、R. Helm、R. Johnson 和 J. Vissides 所著的《设计模式:可复用面向对象软件的基础》,G. Booch 所著的《面向对象分析与设计》等。

方法简写

在对象字面量中,有一种更短的(声明)方法的语法:

// 这些对象作用一样

user = {
  sayHi: function() {
    alert("Hello");
  }
};

// 方法简写看起来更好,对吧?
let user = {
  sayHi() { // 与 "sayHi: function(){...}" 一样
    alert("Hello");
  }
};

如上所示,我们可以省略 "function",只写 sayHi()

说实话,这种表示法还是有些不同。在对象继承方面有一些细微的差别(稍后将会介绍),但目前它们并不重要。在几乎所有的情况下,更短的语法是首选的。

方法中的 “this”

通常,对象方法需要访问对象中存储的信息才能完成其工作。

例如,user.sayHi() 中的代码可能需要用到 user 的 name 属性。

为了访问该对象,方法中可以使用 this 关键字。

this 的值就是在点之前的这个对象,即调用该方法的对象。

举个例子:

let user = {
  name: "John",
  age: 30,

  sayHi() {
    // "this" 指的是“当前的对象”
    alert(this.name);
  }

};

user.sayHi(); // John

在这里 user.sayHi() 执行过程中,this 的值是 user

技术上讲,也可以在不使用 this 的情况下,通过外部变量名来引用它:

let user = {
  name: "John",
  age: 30,

  sayHi() {
    alert(user.name); // "user" 替代 "this"
  }

};

……但这样的代码是不可靠的。如果我们决定将 user 复制给另一个变量,例如 admin = user,并赋另外的值给 user,那么它将访问到错误的对象。

下面这个示例证实了这一点:

let user = {
  name: "John",
  age: 30,

  sayHi() {
    alert( user.name ); // 导致错误
  }

};


let admin = user;
user = null; // 重写让其更明显

admin.sayHi(); // TypeError: Cannot read property 'name' of null

如果我们在 alert 中以 this.name 替换 user.name,那么代码就会正常运行。

“this” 不受限制

在 JavaScript 中,this 关键字与其他大多数编程语言中的不同。JavaScript 中的 this 可以用于任何函数,即使它不是对象的方法。

下面这样的代码没有语法错误:

function sayHi() {
  alert( this.name );
}

this 的值是在代码运行时计算出来的,它取决于代码上下文。

例如,这里相同的函数被分配给两个不同的对象,在调用中有着不同的 “this” 值:

let user = { name: "John" };
let admin = { name: "Admin" };

function sayHi() {
  alert( this.name );
}

// 在两个对象中使用相同的函数
user.f = sayHi;
admin.f = sayHi;

// 这两个调用有不同的 this 值
// 函数内部的 "this" 是“点符号前面”的那个对象
user.f(); // John(this == user)
admin.f(); // Admin(this == admin)

admin['f'](); // Admin(使用点符号或方括号语法来访问这个方法,都没有关系。)

这个规则很简单:如果 obj.f() 被调用了,则 thisf 函数调用期间是 obj。所以在上面的例子中 this 先是 user,之后是 admin

在没有对象的情况下调用:this == undefined

我们甚至可以在没有对象的情况下调用函数: