原型和原型链
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
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
console.log(zhangsan.__proto__); // 隐式原型 // Person {constructor: ƒ, learn: ƒ}
console.log(Student.prototype); // 显式原型 // Person {constructor: ƒ, learn: ƒ}
console.log(zhangsan.__proto__ == Student.prototype) // true
2
3
# “函数即对象”
函数是第一类对象
- 在JavaScript中,对象所拥有的功能,函数一样拥有。
- 函数也是对象,唯一不同的地方在于,函数是可以调用的(invokable),也就是说函数会被调用以便执行某项动作。
- 函数式编程更易测试、扩展和模块化。
① 当任意一个普通函数用于创建一类对象时,它就被称作构造函数,或构造器。
function Person() {
}
var person1 = new Person();
var person2 = new Person();
console.log(person1)
console.log(person2)
2
3
4
5
6
② 可以通过对象.constructor
拿到创建该实例对象的构造函数。
function Person() {
}
var person1 = new Person();
console.log(person1.constructor)
// ƒ Person() {
// }
2
3
4
5
6
③ Function函数和Object函数是JS内置对象,也叫内部类,JS自己封装好的类.
④ 原型对象即实例对象自己构造函数内的prototype对象。
# 理解"函数即对象"
function Person() {
}
console.log(Person.constructor) // 输出结果:ƒ Function() { [native code] }
// 上面是普通函数声明方法,生成具名函数,在声明时就已经生成对象模型。
console.log(Function.constructor) // 输出结果:ƒ Function() { [native code] }
console.log(Object.constructor) // 输出结果:ƒ Function() { [native code] }
2
3
4
5
6
7
Person虽被声明为一个函数,但它同样可以通过Person.constructor
输出内容。输出内容说明Function函数是Person函数[普通声明的函数]的构造函数。
Function函数同时是自己的构造函数。
Function函数同样是Object这类内置对象的构造函数。
上面的声明函数的代码其实几乎等同于下面代码:
// 使用Function构造器创建Function对象
var Person = new Function()
console.log(Person)
// ƒ anonymous(
// ) {
// }
// 几乎?因为这种方式生成的函数是匿名函数[anonymous],并且只在真正调用时才生成对象模型。
2
3
4
5
6
7
8
在JS里,函数就是Function函数的实例对象。
总结:对象由函数创建,函数都是Function对象实例。
# 构造函数
constructor
返回创建实例对象时构造函数的引用。此属性的值是对函数本身的引用,而不是一个包含函数名称的字符串。
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] }
2
3
4
5
6
7
8
9
构造函数本身就是一个函数,与普通函数没有任何区别,不过为了规范一般将其首字母大写。构造函数和普通函数的区别在于,使用 new
生成实例的函数就是构造函数,直接调用的就是普通函数。
普通函数创建的实例也有 constructor
属性
// 普通函数
function Person(age) {
return {
age: age
}
}
var p3 = Person(20);
console.log(p3.constructor === Object); // true
2
3
4
5
6
7
8
# 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()
即可。
// 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] }
2
3
4
这里的 constructor
属性来自哪里?其实是 Symbol
原型上的,即 Symbol.prototype.constructor
返回创建实例原型的函数, 默认为 Symbol
函数。
# constructor 值只读吗
对于引用类型来说 constructor
属性值是可以修改的,但是对于基本类型来说是只读的。
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);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 原型
每个对象拥有一个原型对象,对象以其原型为模板,从原型继承方法和属性,这些属性和方法定义在对象的构造器函数的 prototype
属性上,而非对象实例本身。
fun
对象有一个原型对象 fun.prototype
,其上有两个属性,分别是 constructor
和 __proto__
,其中 __proto__
已被弃用。
# 待整理
Symbol
作为构造函数来说并不完整,因为不支持语法 new Symbol()
,但其原型上拥有 constructor
属性,即 Symbol.prototype.constructor
。
引用类型 constructor
属性值是可以修改的,但是对于基本类型来说是只读的,当然 null
和 undefined
没有 constructor
属性。
__proto__
是每个实例上都有的属性,prototype
是构造函数的属性,这两个并不一样,但 p.__proto__
和 Parent.prototype
指向同一个对象。
__proto__
属性在 ES6
时被标准化,但因为性能问题并不推荐使用,推荐使用 Object.getPrototypeOf()
。
每个对象拥有一个原型对象,通过 __proto__
指针指向上一个原型 ,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层一层,最终指向 null
,这就是原型链。