原型的定义
- 在JavaScript中,任何一个函数,都有一个prototype属性,指向一个对象。
- 输出了一个函数的prototype属性,你会发现是一个空对象。输出这个prototype的类型,发现是object类型
- prototype就是英语“原型”的意思。每个函数都有原型,原型是一个对象
function fun(){
alert("Hi");
}
console.log(fun.prototype); // Object{}
console.log(typeof fun.prototype); // Object
// 构造函数,构造函数里面没有任何语句,也就是说,
// 这个构造函数在执行的时候,不会给创建出来的对象添加任何属性。
function Person() {
}
// 构造函数的原型,我们更改了构造函数的原型,为一个新的对象:
Person.prototype = {
name: "蔡徐坤",
sex: "男",
age: 18
}
// 当一个对象被new出来的时候,不仅仅执行了构造函数里面的语句,
// 也会把这个函数__proto__指向构造函数的Person。
let obj= new Person();
console.log(obj.__proto__); // {name: "蔡徐坤", sex: "男", age: 18}
console.log(obj.__proto__ == Person.prototype); // true
//当我们试图访问name、sex、age属性的时候,身上没有
// 那么就去查找原型,原型身有,就当做了自己的属性返回了
console.log(obj.name); // 蔡徐坤
console.log(obj.sex); // 男
console.log(obj.age); // 18
prototype一定是函数的属性!当这个函数是一个构造函数的时候,那么它new出来的对象,将以它的原型那个对象为new出来的实例的原型对象
注意
任何一个对象,都有__proto__属性,这个属性指向自己的原型对象
原型的用途
定义一个方法的时候,如果写在构造函数里面:
function Person(name, age) {
this.name = name;
this.age = age;
this.say = function () {
console.log("你好,我是" + this.name + "我今年" + this.age + "岁了");
}
}
let obj1 = new Person("菜虚鲲", 12);
let obj2 = new Person("踩墟捆", 11);
obj1.say(); // 你好,我是菜虚鲲我今年12岁了
obj2.say(); // 你好,我是踩墟捆我今年11岁了
实际上,这个函数被复制了两份,一份给了obj1,一份给了obj2, obj1和obj2这两个实例身上有了相同功能的函数,但是这个函数不是同一个函数
console.log(obj1.say == obj2.say); //false
那该如何解决这个问题呢, 一句话:所有的属性要绑在对象身上,而所有的方法定义在对象的原型对象中:
function Person(name, age) {
// 构造函数里面,负责定义一些属性,随着构造函数的执行,这些属性将绑定到new出来的对象身上
this.name = name;
this.age = age;
}
// 把所有的方法,定义在原型对象身上:
Person.prototype.say = function () {
console.log("你好,我是" + this.name + "我今年" + this.age + "岁了");
}
let obj1 = new Person("菜虚鲲", 12);
let obj2 = new Person("踩墟捆", 11);
obj1.say(); // 你好,我是菜虚鲲我今年12岁了
obj2.say(); // 你好,我是踩墟捆我今年11岁了
证明了obj1的say方法和obj1的say方法,是同一个函数 , 内存消耗小很多
console.log(obj1.say == obj2.say); // true
原型的特点
- 存储在prototype中的方法可以被对应构造函数创建出来的所有对象共享
- prototype中除了可以存储方法以外, 还可以存储属性
- prototype如果出现了和构造函数中同名的属性或者方法, 对象在访问的时候, 访问到的是构造函中的数据
function Person(name, age) {
this.name = name;
this.age = age;
this.currentType = "构造函数中的type";
this.say = function () {
console.log("构造函数中的say");
}
}
Person.prototype = {
currentType: "人",
say: function () {
console.log("hello world");
}
}
let obj1 = new Person("菜虚鲲", 34);
obj1.say(); // 构造函数中的say
console.log(obj1.currentType); // 构造函数中的type
let obj2 = new Person("踩墟捆", 44);
obj2.say(); // 构造函数中的say
console.log(obj2.currentType); // 构造函数中的type
- 在给一个对象不存在的属性设置值的时候, 不会去原型对象中查找, 如果当前对象没有就会给当前对象新增一个不存在的属性
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype = {
constructor: Person,
currentType: "人",
say: function () {
console.log("hi");
}
}
let obj = new Person("蔡徐坤", 12);
obj.currentType = "新设置的值";
console.log(obj.currentType); // 新设置的值
console.log(obj.__proto__.currentType); // "人"
原型链
Function函数
- JavaScript中函数是引用类型(对象类型), 既然是对象, 所以也是通过构造函数创建出来的, "所有函数"都是通过Function构造函数创建出来的对象
- JavaScript中只要是"函数"就有prototype属性
- Function函数"的prototype属性指向"Function原型对象"
- JavaScript中只要是"原型对象"就有constructor属性
- "Function原型对象"的constructor指向它对应的构造函数
- Person构造函数是Function构造函数的实例对象, 所以也有__proto__属性
- Person构造函数的__proto__属性指向"Function原型对象"
function Person(name, age) {
this.name = name;
this.age = age;
}
let obj1 = new Person('踩墟捆', 12);
console.log(Function); // ƒ Function() { [native code] }
console.log(Function.prototype); // ƒ () { [native code] }
console.log(Function.prototype.constructor); // ƒ Function() { [native code] }
console.log(Function === Function.prototype.constructor); // true
console.log(Person.__proto__); // ƒ () { [native code] }
console.log(Person.__proto__ === Function.prototype); // true
Object函数
- Object是一个函数,是系统内置的构造函数,用于创造对象的 , Object.prototype是所有对象的原型链终点
所以,当我们在一个对象上打点调用某个方法的时候,系统会沿着原型链去寻找它的定义,一直找到Object.prototype
function Person(name, age) {
this.name = name;
this.age = age;
}
let obj1 = new Person('踩墟捆', 19);
console.log(Function.__proto__); // ƒ () { [native code] }
console.log(Function.__proto__ === Function.prototype); // true
console.log(Object); // ƒ Object() { [native code] }
console.log(Object.__proto__); // ƒ () { [native code] }
console.log(Object.__proto__ === Function.prototype); // true
console.log(Object.prototype); // {constructor: ƒ,
__defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ,
__lookupGetter__: ƒ, …}
console.log(Object.prototype.constructor); // ƒ Object() { [native code] }
console.log(Object.prototype.constructor === Object); // true
console.log(Object.prototype.__proto__); // null
函数对象关系
- 所有的构造函数都有一个prototype属性, 所有prototype属性都指向自己的原型对象
- 所有的原型对象都有一个constructor属性, 所有constructor属性都指向自己的构造函数
- 所有函数都是Function构造函数的实例对象(Function函数是所有函数的祖先函数)
- 所有函数都是对象, 包括Function构造函数
- 所有对象都有__proto__属性
- 普通对象的__proto__属性指向创建它的那个构造函数对应的"原型对象"
- 所有对象的__proto__属性最终都会指向"Object原型对象"
- "Object原型对象"的__proto__属性指向NULL
function Person(name, age) {
this.name = name;
this.age = age;
}
let obj1 = new Person('踩墟捆', 12);
console.log(Function.prototype.__proto__); // Object
console.log(Person.prototype.__proto__); // Object
console.log(Function.prototype.__proto__ === Person.prototype.__proto__); // true
console.log(Function.prototype.__proto__ === Object.prototype); //true
console.log(Person.prototype.__proto__ === Object.prototype); // true
原型链机制
- 对象中__proto__组成的链条我们称之为原型链
- 对象在查找属性和方法的时候, 会先在当前对象查找
- 如果当前对象中找不到想要的, 会依次去上一级原型对象中查找 , 如果找到Object原型对象都没有找到, 就会报错
function Person(name, age) {
this.name = name;
this.age = age;
this.currentType = "构造函数中的type";
}
Person.prototype = {
// 注意点: 为了不破坏原有的关系, 在给prototype赋值的时候
// 需要在自定义的对象中手动的添加constructor属性, 手动的指定需要指向谁
constructor: Person,
// 构造函数中的属性或方法会替换原型对象中的属性或方法
currentType: "人",
say: function () {
console.log("Hi");
}
}
let obj1 = new Person("lnj", 34);
console.log(obj1.currentType); // 构造函数中的type
console.log(Person.prototype.constructor); // ƒ Person(name, age) {...}