提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
由来
在理解原型和原型链之前,首先我们要知道为什么JS中会有原型和原型链。
产生原型和原型链是有什么问题需要被解决吗?顺着这个思路我们开始本次的探讨:
学习过基本js的人都知道在js中存在一个垃圾回收机制,它会将我们不需要的数据类型声明进行回收,把不需要的数据进行清除,释放内存空间。而这个回收机制是如何判定哪些数据是我们不需要的呢?首先原始数据类型和对象的引用地址都是在栈内存空间开辟一个空间来存储的,而对象的引用地址的数据是在堆内存空间中开辟一个空间进行存储。二者之间由栈内存空间指向堆内存空间。若栈内存没有指向堆内存空间,则堆内存空间的数据就会被判定为不需要的数据,从而将该数据清除。
而每一个原始数据类型和对象都有一系列的方法和属性,但是它们的属性和方法又大都相同,这导致存储时极其占用空间。作为一个程序员是不允许这种冗余出现的。就好比CSS样式中,我们会写公共样式来减少代码的冗余。这时,我们就需要将这些声明的数据类型中公有的属性和方法单独存放在一个地方,使得每个数据类型都可以调用。这就是原型(prototype)的由来。
概念
原型对象:构造函数的prototype属性指向一个对象,这个对象是该构造函数实例化出来的对象的原型对象。即具有公共属性和方法的对象。
原型链:原型对象也有一个原型,以此类推就形成了原型链。基本思想就是让一个引用对象继承另一个引用对象的属性和方法。
js是基于原型的面对对象,原型也是对象,用来产生其他对象
目的
为函数提供一个声明属性和方法的公共区域,使得这个函数创建的实例对象都能访问到,以此减少内存消耗。
作用
- 数据共享,节省内存空间,公有方法一般放原型链
- 实现继承
代码如下(示例):
<script>
console.log(Object.prototype);
console.log(Array.prototype);
var arr = [1];
console.log(arr);
// arr.concat
console.log(String.prototype);
console.log(Number.prototype);
console.log(Boolean.prototype);
console.log(Function.prototype);
</script>
<script>
function Person(name, age) {
this.name = name;
this.age = age;
/* this.getName = function () {
return this.name;
};
this.getAge = function () {
return this.name;
}; */
}
Person.prototype.getName = function () {
return this.name;
};
Person.prototype.getAge = function () {
return this.age;
};
var per1 = new Person('张三', 28);
var per2 = new Person('李四', 22);
per1.getName = function () {
return '111+张三';
};
console.log(per1);
console.log(per1.getName());
console.log(per2.getName());
console.log(per1.getAge()); // 28 per1.__proto__ ===> Person.prototype
console.log(per2.getAge()); // 22 per1.__proto__ ===> Person.prototype
</script>
类别
- 内置对象:js中自带的对象,如Date、Math、Array 等
- 自定义对象:开发人员自己定义生成的对象
- 宿主对象:只有在特定的环境下才会有的对象,比如:widnow、navigator、history 等对象,这些都是依赖于浏览器环境的
new关键字的步骤
- 创建了一个新对象
- 将构造函数的this指向了这个新对象
- 将新对象的__proto__属性 指向构造函数 的prototype属性 (构造函数的原型对象)
- 执行构造函数
- 返回this
<script>
/*
1. 新对象都有一个属性 叫__proto__
2. 构造函数都有一个属性 叫prototype
3. 构造函数的原型对象上有一个属性 叫constructor 指向构造函数
4. 所有的原型对象都是Object函数对象构造出来的(除Object本身的原型对象之外)
*/
console.log(person1.__proto__ === Person.prototype); // true
console.log(Person.prototype.constructor === Person); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__); // null
console.log(person1.__proto__.__proto__ === Object.prototype); // true
console.log(person1.__proto__.__proto__.__proto__ === null); // true
// console.log(person1.toString());
console.log(Person.prototype.toString);
console.log(Person.prototype);
console.log(Object.prototype);
// 5. 直接量对象 都是Object函数对象构造出来的
var obj = {};
console.log(obj); // obj.__proto__ === Object.prototype
function Person() {
this.name = 'qwer';
}
// Person是一个函数 Person是一个对象
Person.ss = '新增的ss属性';
console.dir(Person);
console.dir(Person.ss);
var per1 = new Person();
console.log(per1);
// 1. 所有的函数都是Function函数对象构造的
console.log(Person.__proto__ === Function.prototype); // true
console.log(Function.prototype.__proto__ === Object.prototype); // true
console.log(Function.prototype.__proto__.__proto__ === null); // true
console.log(Person.__proto__.__proto__.__proto__ === null); // true
// 2. Function顶层没有人构造 自己构造了自己
console.log(Function.__proto__ === Function.prototype); // true
// console.log(Function.prototype.__proto__ === Object.prototype);
console.log(Function.__proto__.__proto__ === Object.prototype); // true
console.log(Object.__proto__ === Function.prototype); // true
// console.log(Function.prototype.__proto__ === Object.prototype);
console.log(Object.__proto__.__proto__ === Object.prototype); // true
console.log(Object.__proto__.__proto__.__proto__); // null
console.log(Function.__proto__.__proto__.__proto__); // null
</script>
原型链的指向
原型链的相关练习题
<script src>
function Person() {
this.name = 'qwer';
}
var person1 = new Person();
Person.prototype.w = '这是w属性';
Function.prototype.q = '这是q属性';
Object.prototype.e = '这是e属性';
console.log(person1.w); // person1.__proto__.__proto__.__proto__
console.log(person1.q); // undefined
console.log(person1.e);
console.log(Person.w); // undefined Person.__proto__.__proto__.__proto__
console.log(Person.q);
console.log(Person.e);
console.log(Function.q); // Function.__proto__.__proto__
console.log(Function.e);
console.log(Object.q); // Object.__proto__.__proto__
console.log(Object.e);
</script>
<script src>
function Foo() {
Foo.a = function () {
console.log(1);
};
this.a = function () {
console.log(2);
};
}
Foo.prototype.a = function () {
console.log(3);
};
Foo.a = function () {
console.log(4);
};
Foo.a(); // 4
var obj = new Foo();
obj.a(); // 2
Foo.a(); // 1
</script>
<script src>
function One() {}
function Two() {}
function Three() {}
Two.prototype = new One();
Three.prototype = new Two();
var three = new Three();
console.log(three.__proto__ === Three.prototype); // Three.prototype.__proto__
console.log(three.__proto__.__proto__ === Two.prototype);
console.log(three.__proto__.__proto__.__proto__ === One.prototype); // Two.prototype.__proto__
console.log(three.__proto__.__proto__.__proto__.__proto__ === Object.prototype); // Two.prototype.__proto__
</script>
<script>
function SuperType(name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
SuperType.prototype.sayName = function () {
console.log(this.name);
};
function SubType(name, age) {
SuperType.call(this, name);
this.age = age;
}
SubType.prototype = new SuperType();
SubType.prototype.sayAge = function () {
console.log(this.age);
};
var instance1 = new SubType('Nicholas', 29); // {age: 29,name:'Nicholas',colors:['red', 'blue', 'green', 'black']}
instance1.colors.push('black');
console.log(instance1.colors); // ['red', 'blue', 'green', 'black']
instance1.sayName(); // 'Nicholas' instance1.__proto__ SubType.prototype.__proto__ === SuperType.prototype
instance1.sayAge(); // 29
var instance2 = new SubType('Greg', 27);
console.log(instance2.colors); // ['red', 'blue', 'green']
instance2.sayName(); // 'Greg'
instance2.sayAge(); // 27
</script>