对象通常用于表示现实世界中的实体,例如用户、订单等
let user = {
name: "John",
age: 30
};
在现实世界中,用户可以执行操作:从购物车中选择商品、登录、注销等。
在 JavaScript 中,操作由属性中的函数表示。
方法示例
首先,让我们教user
打招呼
let user = {
name: "John",
age: 30
};
user.sayHi = function() {
alert("Hello!");
};
user.sayHi(); // Hello!
这里我们刚刚使用函数表达式创建了一个函数,并将其分配给对象的属性user.sayHi
。
然后我们可以将其称为 user.sayHi()
。用户现在可以说话了!
作为对象属性的函数称为其方法。
因此,这里我们得到了对象 user
的方法 sayHi
。
当然,我们可以像这样使用预先声明的函数作为方法
let user = {
// ...
};
// first, declare
function sayHi() {
alert("Hello!");
}
// then add as a method
user.sayHi = sayHi;
user.sayHi(); // Hello!
当我们使用对象来表示实体编写代码时,这称为面向对象编程,简称:“OOP”。
OOP 是一个大东西,它本身就是一门有趣的科学。如何选择合适的实体?如何组织它们之间的交互?那是架构,并且有关于该主题的伟大书籍,例如 E. Gamma、R. Helm、R. Johnson、J. Vissides 撰写的“设计模式:可重用面向对象软件的元素”或 G. Booch 撰写的“面向对象分析和设计与应用”,等等。
方法简写
对象字面量中方法的语法更简短
// these objects do the same
user = {
sayHi: function() {
alert("Hello");
}
};
// method shorthand looks better, right?
user = {
sayHi() { // same as "sayHi: function(){...}"
alert("Hello");
}
};
如所示,我们可以省略 "function"
,仅编写 sayHi()
。
说实话,这些符号并不完全相同。与对象继承相关的细微差别(稍后会介绍),但现在它们并不重要。在几乎所有情况下,都首选较短的语法。
方法中的“this”
对象方法通常需要访问存储在对象中的信息才能完成其工作。
例如,user.sayHi()
中的代码可能需要 user
的名称。
要访问对象,方法可以使用 this
关键字。
this
的值是“点之前”的对象,用于调用该方法的对象。
例如
let user = {
name: "John",
age: 30,
sayHi() {
// "this" is the "current object"
alert(this.name);
}
};
user.sayHi(); // John
在此,在执行 user.sayHi()
期间,this
的值将为 user
。
从技术上讲,也可以通过外部变量引用对象来访问对象,而无需 this
let user = {
name: "John",
age: 30,
sayHi() {
alert(user.name); // "user" instead of "this"
}
};
…但这种代码不可靠。如果我们决定将 user
复制到另一个变量,例如 admin = user
,并用其他内容覆盖 user
,那么它将访问错误的对象。
以下对此进行了演示
let user = {
name: "John",
age: 30,
sayHi() {
alert( user.name ); // leads to an error
}
};
let admin = user;
user = null; // overwrite to make things obvious
admin.sayHi(); // TypeError: Cannot read property 'name' of null
如果我们在 alert
中使用 this.name
而不是 user.name
,那么代码将起作用。
“this”未绑定
在 JavaScript 中,关键字 this
的行为与大多数其他编程语言不同。它可以在任何函数中使用,即使它不是对象的方法。
在以下示例中没有语法错误
function sayHi() {
alert( this.name );
}
this
的值在运行时根据上下文进行计算。
例如,这里将同一个函数分配给两个不同的对象,并且在调用中具有不同的“this”
let user = { name: "John" };
let admin = { name: "Admin" };
function sayHi() {
alert( this.name );
}
// use the same function in two objects
user.f = sayHi;
admin.f = sayHi;
// these calls have different this
// "this" inside the function is the object "before the dot"
user.f(); // John (this == user)
admin.f(); // Admin (this == admin)
admin['f'](); // Admin (dot or square brackets access the method – doesn't matter)
规则很简单:如果调用 obj.f()
,则在调用 f
期间 this
为 obj
。因此,在上面的示例中,它要么是 user
,要么是 admin
。
this == undefined
我们甚至可以在没有对象的情况下调用函数
function sayHi() {
alert(this);
}
sayHi(); // undefined
在这种情况下,在严格模式中 this
为 undefined
。如果我们尝试访问 this.name
,将出现错误。
在非严格模式中,在这种情况下 this
的值将是全局对象(浏览器中的 window
,我们将在本章后面 全局对象 中介绍)。这是一个历史行为,"use strict"
修复了此行为。
通常,此类调用是编程错误。如果函数内部有 this
,则它期望在对象上下文中调用。
this
的后果如果您来自另一种编程语言,那么您可能习惯于“绑定 this
”的概念,其中在对象中定义的方法始终具有引用该对象的 this
。
在 JavaScript 中,this
是“自由”的,其值在调用时进行评估,并且不取决于方法声明的位置,而是取决于“点之前”的对象。
在运行时评估 this
的概念既有优点也有缺点。一方面,一个函数可以被不同的对象重用。另一方面,更大的灵活性为错误创造了更多可能性。
我们的立场不是判断这种语言设计决策的好坏。我们将了解如何使用它,如何从中受益以及如何避免问题。
箭头函数没有“this”
箭头函数很特殊:它们没有“自己的”this
。如果我们从这样的函数中引用 this
,它将从外部“普通”函数中获取。
例如,这里 arrow()
使用外部 user.sayHi()
方法中的 this
let user = {
firstName: "Ilya",
sayHi() {
let arrow = () => alert(this.firstName);
arrow();
}
};
user.sayHi(); // Ilya
这是箭头函数的一个特殊功能,当我们实际上不想拥有单独的 this
,而是想从外部上下文中获取它时,它很有用。在本章后面 重新审视箭头函数 中,我们将更深入地了解箭头函数。
总结
- 存储在对象属性中的函数称为“方法”。
- 方法允许对象像
object.doSomething()
一样“执行”。 - 方法可以将对象引用为
this
。
this
的值在运行时定义。
- 声明函数时,它可以使用
this
,但在调用函数之前,该this
没有值。 - 函数可以在对象之间复制。
- 在“方法”语法中调用函数时:
object.method()
,调用期间this
的值是object
。
请注意,箭头函数很特殊:它们没有 this
。在箭头函数中访问 this
时,它取自外部。
评论
<code>
标记,对于多行代码,请用<pre>
标记将其包装起来,对于 10 行以上的代码,请使用沙盒 (plnkr、jsbin、codepen…)