原型和原型链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Person{
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHello() {
console.log(`您好 ${this.name}`);
}
}
// 子类继承
class Student extends Person {
constructor(name, age, sex) {
super(name, age);
this.sex = sex;
}
learn() {
console.log(`我是${this.name},今年${this.age}岁,${this.sex},在学英语`);
}
}
// 实例化
const zhangsan = new Student("张三",18,"男");
zhangsan.sayHello(); // 您好 张三
zhangsan.learn(); // 我是张三,今年18岁,男,在学英语
console.log(zhangsan);
console.log(zhangsan instanceof Student); // true
console.log(zhangsan instanceof Person); // true

image-20230413133244095

1
2
3
console.log(zhangsan.__proto__); // 隐式原型 // Person {constructor: ƒ, learn: ƒ}
console.log(Student.prototype); // 显式原型 // Person {constructor: ƒ, learn: ƒ}
console.log(zhangsan.__proto__ == Student.prototype) // true

image-20230413134048568

image-20230413134105002

“函数即对象”

函数是第一类对象

  • 在JavaScript中,对象所拥有的功能,函数一样拥有。
  • 函数也是对象,唯一不同的地方在于,函数是可以调用的(invokable),也就是说函数会被调用以便执行某项动作。
  • 函数式编程更易测试、扩展和模块化。

① 当任意一个普通函数用于创建一类对象时,它就被称作构造函数,或构造器。

1
2
3
4
5
6
function Person() {
}
var person1 = new Person();
var person2 = new Person();
console.log(person1)
console.log(person2)

image-20230404165108814

② 可以通过对象.constructor拿到创建该实例对象的构造函数。

1
2
3
4
5
6
function Person() {
}
var person1 = new Person();
console.log(person1.constructor)
// ƒ Person() {
// }

③ Function函数和Object函数是JS内置对象,也叫内部类,JS自己封装好的类.

④ 原型对象即实例对象自己构造函数内的prototype对象。

理解”函数即对象”

1
2
3
4
5
6
7
function Person() {

}
console.log(Person.constructor) // 输出结果:ƒ Function() { [native code] }
// 上面是普通函数声明方法,生成具名函数,在声明时就已经生成对象模型。
console.log(Function.constructor) // 输出结果:ƒ Function() { [native code] }
console.log(Object.constructor) // 输出结果:ƒ Function() { [native code] }

Person虽被声明为一个函数,但它同样可以通过Person.constructor输出内容。输出内容说明Function函数是Person函数[普通声明的函数]的构造函数。

Function函数同时是自己的构造函数。

Function函数同样是Object这类内置对象的构造函数。

上面的声明函数的代码其实几乎等同于下面代码:

1
2
3
4
5
6
7
8
// 使用Function构造器创建Function对象
var Person = new Function()
console.log(Person)
// ƒ anonymous(
// ) {

// }
// 几乎?因为这种方式生成的函数是匿名函数[anonymous],并且只在真正调用时才生成对象模型。

在JS里,函数就是Function函数的实例对象。

总结:对象由函数创建,函数都是Function对象实例。

构造函数

constructor 返回创建实例对象时构造函数的引用。此属性的值是对函数本身的引用,而不是一个包含函数名称的字符串。

1
2
3
4
5
6
7
8
9
function Person(age) {
this.age = age;
}
var person = new Person(20);
console.log(person.constructor == Person) // true
console.log(person.constructor == Object) // false
console.log(Person.constructor == Function) // true
console.log(Function.constructor == Object) // false
console.log(Function.constructor) // ƒ Function() { [native code] }

构造函数本身就是一个函数,与普通函数没有任何区别,不过为了规范一般将其首字母大写。构造函数和普通函数的区别在于,使用 new 生成实例的函数就是构造函数,直接调用的就是普通函数。

普通函数创建的实例也有 constructor 属性

1
2
3
4
5
6
7
8
// 普通函数
function Person(age) {
return {
age: age
}
}
var p3 = Person(20);
console.log(p3.constructor === Object); // true

Symbol是构造函数吗?

The Symbol() function returns a value of type symbol, has static properties that expose several members of built-in objects, has static methods that expose the global symbol registry, and resembles a built-in object class but is incomplete as a constructor because it does not support the syntax “new Symbol()“.

Symbol()函数返回类型为Symbol的值,具有公开内置对象的几个成员的静态财产,具有公开全局符号注册表的静态方法,类似于内置对象类,但作为构造函数是不完整的,因为它不支持语法“new Symbol”。

Symbol 是基本数据类型,但作为构造函数来说它并不完整,因为它不支持语法 new Symbol(),Chrome 认为其不是构造函数,如果要生成实例直接使用 Symbol() 即可。

1
2
3
4
// new Symbol(123); // Uncaught TypeError: Symbol is not a constructor
var sym = Symbol(123);
console.log(sym); // Symbol(123)
console.log(sym.constructor); // ƒ Symbol() { [native code] }

这里的 constructor 属性来自哪里?其实是 Symbol 原型上的,即 Symbol.prototype.constructor 返回创建实例原型的函数, 默认为 Symbol 函数。

constructor 值只读吗

对于引用类型来说 constructor 属性值是可以修改的,但是对于基本类型来说是只读的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function Foo() {
this.value = 1;
}
Foo.prototype = {
method: function (){}
};
function Bar() {}

console.log(Bar.prototype.constructor) // ƒ Bar() {}

// 设置Bar的prototype属性为Foo的实例对象
Bar.prototype = new Foo();
Bar.prototype.foo = "hello"

console.log(Bar.prototype.constructor) // ƒ Object() { [native code] }

// 修正 Bar.prototype.constructor 为 Bar 本身
Bar.prototype.constructor = Bar;

var test = new Bar() // 创建 Bar 的一个新实例
console.log(test);

image-20230413130353925

原型

每个对象拥有一个原型对象,对象以其原型为模板,从原型继承方法和属性,这些属性和方法定义在对象的构造器函数的 prototype 属性上,而非对象实例本身。

image-20230413130938482

fun 对象有一个原型对象 fun.prototype,其上有两个属性,分别是 constructor__proto__,其中 __proto__ 已被弃用。

image-20230413131337138

待整理

Symbol 作为构造函数来说并不完整,因为不支持语法 new Symbol(),但其原型上拥有 constructor 属性,即 Symbol.prototype.constructor

引用类型 constructor 属性值是可以修改的,但是对于基本类型来说是只读的,当然 nullundefined 没有 constructor 属性。

__proto__ 是每个实例上都有的属性,prototype 是构造函数的属性,这两个并不一样,但 p.__proto__ Parent.prototype 指向同一个对象。

__proto__ 属性在 ES6 时被标准化,但因为性能问题并不推荐使用,推荐使用 Object.getPrototypeOf()

每个对象拥有一个原型对象,通过 __proto__ 指针指向上一个原型 ,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层一层,最终指向 null,这就是原型链。


原型和原型链
http://example.com/2023/04/04/07.前端小课堂/05.原型和原型链/
作者
Deng ErPu
发布于
2023年4月4日
许可协议