原型链
原型链的概念最早由 JavaScript 的创造者 Brendan Eich 发明,
在 JavaScript 中,每个对象都有一个内部链接到另一个对象的属性,该对象被称为原型。如果我们访问一个对象的属性或方法,但该对象本身没有定义这个属性或方法,JavaScript 引擎会沿着原型链向上查找,直到找到该属性或方法或者到达原型链的顶端(通常是 Object.prototype)。
每个对象都有proto属性,但是只有函数对象才有 prototype 属性
// 函数对象
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function () {
console.log("Hello, my name is " + this.name);
};
// 普通对象
var obj = {
prop: "value",
};
console.log(typeof Person); // 输出:function
console.log(typeof obj); // 输出:object
console.log(obj.__proto__); // 输出:Object.prototype
console.log(Person.prototype); // 输出:Person.prototype
console.log(obj.hasOwnProperty("prop")); // 输出:true
var person = new Person("Alice");
person.sayHello(); // 输出:Hello, my name is Alice
"new"关键字用于创建一个对象实例,同时将该对象的原型链连接到构造函数的原型对象上。
- 创建一个空对象。
- 将该空对象的原型指向构造函数的原型对象(即构造函数.prototype)。
- 将构造函数内部的 this 指向该空对象。
- 执行构造函数内部的代码,为该空对象添加属性和方法。
- 返回该空对象作为"new"操作符的结果。
ES6 类语法
ES2015(也称为 ES6)引入了类语法,使得定义和使用对象更加简洁和易读。 使用 class 关键字定义的类实际上也是一种函数对象。虽然我们通常称其为类对象,但在语言层面上,它仍然是一个特殊的函数对象。
class Person {
constructor(name) {
this.name = name;
}
sayHello() {
console.log("Hello, my name is " + this.name);
}
}
console.log(typeof Person); // 输出:function
var person = new Person("Alice");
person.sayHello(); // 输出:Hello, my name is Alice
函数对象和类对象在 JavaScript 中有一些相似之处:都是对象、都可以拥有属性和方法、都可以被函数调用、都可以通过原型链继承属性和方法 。
但也有一些关键的区别:
| 差异点 | 函数对象 | 类对象 |
|---|---|---|
| 语法 | 函数声明或函数表达式 | class 关键字 |
| 内部方法 constructor | 无 | 用于创建实例对象和初始化属性 |
| 继承方式 | 通过原型链 | extends 关键字(本质也是原型链) |
| 方法枚举性 | 可枚举 | 默认不可枚举 |
| 静态方法定义 | 无法直接定义静态方法 | 使用 static 关键字定义静态方法 |