JavaScript的原型链是一种特殊的对象连接机制,它用于实现继承和属性查找。理解原型链是理解JavaScript中继承和对象之间关系的关键
在JavaScript中,每个对象都有一个原型(prototype)属性,它指向另一个对象
当我们访问一个对象的属性时,如果该对象本身没有该属性,JavaScript引擎就会去查找原型对象是否有这个属性,如果原型对象也没有,它会继续沿着原型链向上查找,直到找到属性或者到达原型链的顶端,即Object.prototype,这样就形成了一个由对象组成的链条,这就是原型链
// 定义一个构造函数
function Person(name, age) {
this.name = name;
this.age = age;
}
// 在Person的原型上定义一个方法
Person.prototype.sayHello = function () {
console.log("Hello, my name is " + this.name);
};
// 构造函数具有prototype属性
console.log(Person.prototype) // 输出内容包括 sayHello() 方法
// 创建一个实例
const person1 = new Person("Alice", 30);
// 访问实例的属性和方法
console.log(person1.name); // 输出 "Alice"
console.log(person1.age); // 输出 30
person1.sayHello(); // 输出 "Hello, my name is Alice"
自定义构造函数情况特殊
如果直接使用 Person() 调用构造函数而没有使用 new,则构造函数内部的 this 不会被绑定到新的对象实例上
在非严格模式下,this 将默认指向全局对象(在浏览器中为 window 对象)
let per1 = Person("Alice")
let per2 = new Person("Bob")
console.log("per1", per1) // 输出 undefined
console.log(this.name) // 将会输出 Alice(神奇)
console.log(name) // 将会输出 Alice(神奇)
console.log(window.name) // 将会输出 Alice(神奇)
console.log("per2", per2)
非函数对象(字面量方式创建)
const person = { name: "Alice", age: 30 };
//非函数对象不具有prototype属性
console.log("person", person.prototype); // 输出 undefined
基本数据类型
let str = "123"
//基本数据类型不具有prototype属性
console.log("str", str.prototype) // 输出 undefined
基本数据类型是不可变的,它们没有自己的属性和方法,然而,有时我们需要对基本数据类型进行一些对象的操作,比如调用方法或访问属性
这时,JavaScript 会对基本数据类型进行隐式的装箱操作,调用str.length存在隐式装箱操作
过程:
- 创建String类型的一个实例
- 在实例中调用指定方法或属性
- 销毁这个实例
·
console.log("str", str.length) // 输出 3
// 以上过程等价于(显式装箱)
let strObj = new Object(str)
console.log("str", strObj.length) // 输出 3
strObj = null
Function 构造函数动态创建函数
//参数列表:(arg1, arg2, ..., body)
const sum = new Function('a', 'b', 'return a + b;');
console.log(sum(2, 3)); // 输出 5
const greet = new Function('name', 'console.log("Hello, " + name + "!");');
greet('John'); // 输出 "Hello, John!"
在 JavaScript 中,几乎所有的构造函数都是由 Function 构造函数生成的,因为在 JavaScript 中,函数本身也是对象
function fn() { }
let f = new fn()
console.log(fn.constructor === Function) // 输出 true
console.log(Object.constructor === Function) // 输出 true
console.log(String.constructor === Function) // 输出 true
console.log(Person.constructor === Function) // 输出 true
console.log(Function.constructor === Function) // 输出 true
// constructor 在实例对象中是一个属性,这个属性指向构造了这个实例的构造函数
console.log(person1.constructor === Person) // 输出 true
// 而字面量方式创建的对象,它的 constructor 指向 Object
console.log(person.constructor === Function) // 输出 false
console.log(person.constructor === Object) // 输出 true
在JavaScript中,一个对象的__proto__属性指向这个对象的构造函数的 prototype 属性(这个对象的原型)
// fn 是函数对象
console.log(fn.__proto__ === fn.constructor.prototype) // 输出 true
console.log(fn.__proto__ === Function.prototype) // 输出 true
// f 是普通对象
console.log(f.__proto__ === fn.prototype) // 输出 true
console.log(person1.__proto__ === Person.prototype) // 输出 true
// 此外
console.log(fn.prototype.constructor === fn) // 输出 true
console.log(fn.prototype.constructor.prototype === fn.prototype) // 输出 true
console.log(String.__proto__ === Array.__proto__) // 输出 true(?)
// 构造函数的原型是个原型对象
// 既然 fn.prototype 是个对象,那么
console.log(fn.prototype instanceof Object) // 输出 true
// 而一个对象的 __proto__ 为其构造函数的 prototype,如上述所言
console.log(fn.prototype.__proto__ === Object.prototype) // 输出 true
// 函数有 prototype 和 __proto__
console.log(fn.prototype)
console.log(fn.__proto__) // 输出 f () { [native code] }
console.log(Array.prototype)
console.log(Array.__proto__) // 输出 f () { [native code] }
console.log(Function.prototype) // 输出 f () { [native code] }
console.log(Function.__proto__) // 输出 f () { [native code] }
// 对象只有 __proto__,没有 prototype
console.log(f.prototype) // 输出 undefined
console.log(f.__proto__)
// fn 的 prototype 指向自身的原型对象
// 既然是原型对象,就没有 prototype 属性了
console.log(fn.prototype.prototype) // 输出 undefined
// 由于一个对象的 __proto__ 属性指向这个对象的构造函数的 prototype 属性
// 那么
console.log(fn.prototype.__proto__ === Object.prototype) // 输出 true
console.log(fn.prototype.constructor === fn) // 输出 true
console.log(fn.constructor === Function) // 输出 true
原型链的尽头是null
console.log(Object.prototype.__proto__) // 输出 null
console.log(Object.prototype)
person1 的__proto__是 Person 的 prototype
Person.prototype 是个对象
所以说 Person.prototype 这一对象的__proto__,它指向 Object 的 prototype
Object.prototype 是个对象
Object.prototype 这一对象的__proto__,原本应该指向『其构造函数』的 prototype
但由于 Object.prototype 已经是原型链的顶端
所以 Object.prototype 这一对象的__proto__,它指向null,即原型链的尽头
也就是说 Object 不再有原型
console.log(person1.__proto__.__proto__.__proto__) // 输出 null
console.log(Function.prototype.__proto__.__proto__) // 输出 null
Function.prototype 是所有函数对象的原型,构造函数也是一个函数对象,函数对象都继承自 Function.prototype
console.log(fn instanceof Function) // 输出 true
console.log(fn.__proto__ === Function.prototype) // 输出 true
因此构造函数的原型也是 Function.prototype
.__proto__ 访问对象的构造函数的原型
console.log(fn.constructor.__proto__ === Function.prototype)
console.log(fn.constructor.__proto__ === Set.__proto__)
Array.prototype 是 Array 的原型对象,不是函数,因此.__proto__是 Object 的构造函数的 prototype 属性
console.log(Array.prototype.__proto__)
console.log(Array.prototype.__proto__ === Array.prototype.constructor.prototype) // 输出 false
console.log(Array.prototype.__proto__ === Object.prototype) // 输出 true
函数的 prototype.constructor 属性又指回这个函数本身
console.log(Array.prototype.constructor === Array) // 环形引用,输出 true
console.log(Array.prototype.constructor.prototype === Array.prototype) // 环形引用,输出 true
函数的原型是 Function.prototype
console.log(Array.prototype.constructor.__proto__ === Function.prototype) // 输出 true
// String 本身就是构造函数,因此.__proto__直接获取到构造函数的原型
console.log(String.__proto__ === Function.prototype) // 输出 true
console.log(Object.__proto__ === Object.constructor.prototype) // 输出 true
console.log(Object.constructor.prototype === Function.prototype) // 输出 true
console.log(Object.constructor.__proto__ === Function.prototype) // 输出 true
console.log(Object.constructor.prototype === Object.constructor.__proto__) // 输出 true
console.log(Function.__proto__ === Function.prototype) // 输出 true
console.log(Object.__proto__ === Function.__proto__) // 输出 true
总结
每个对象都有 __proto__ 属性,但只有函数对象才有 prototype 属性
,__proto__ 指向创建该对象的构造函数的 prototype
使用构造函数创建对象后,创建的对象不会拥有 prototype 属性,但是它会拥有创建它的构造函数的 prototype 属性内部的所有内容
console.log(Person.prototype)
console.log(person1.prototype) // 输出 undefined
// person1 具有 Person 构造函数 prototype 中的 sayHello() 方法
console.log(person1.sayHello)
当试图访问一个对象的某个属性时,JavaScript 会首先在该对象自身的属性中查找
如果没有找到,它就会沿着原型链往上查找,直到找到为,如果整个原型链上都没有找到这个属性,那么返回 undefined