原型,原型链
原型 prototype
- 是一个对象,叫prototype
- 实例化之前,原型只是挂靠在构造函数上边
- 实例化后,它存在于实例化的对象里面,也就是this里边。
- 放在__proto__容器里面。
- 原型是实例化以后才有的
- Car.prototype是构造函数Car的原型.
- prototype定义了每个构造函数构造出的每个对象的公共祖先。
- 所有被构造函数构造出的对象,都可以继承原型上的属性和方法。
proto
- __proto__属于每个实例化的对象
- 是存储原型的容器 proto: Object.prototype
- 实例化对象的proto是能改的,改变构造函数的原型即可。
原型链
- 沿着__proto__向上,逐层查找原型的属性和方法的链条,就叫做原型链。
- 原型链的最顶端是: Object.prototype
- 实例化出来的对象在原型链的最底端
原型的作用
- 把固定的(也就是写死的)变量值和方法,写在原型上
- 把需要参数赋值的变量值写在构造函数里面
什么时候写在原型上,什么时候写在构造函数里
- 写死的值和方法写在原型上,
- 需要传参的值,写在构造函数里面,
- 所有的方法都写在原型上
//固定的值和方法
var color = 'red';
var fb = function(){
//...
}
插件的写法:
- ;立即执行函数
- 构造函数
- 原型上写东西
- window放到全局
;(function(){
var Test = function(){};
Test.prototype = {
fb:function(){}
}
window.Test = Test;
}();
实例能修改原型吗?
- 实例对象不能修改原型上的原始值
- 能修改引用值的项,因为引用值拿的是同个堆内存的地址。
- 原始值只读,引用值可修改
function Car(){}
Car.prototype = {
name:123,
str:{
age:18
},
arr : [1,2,3]
}
var car = new Car();
car.arr[0] = 100.;
console.log(car.arr);
console.log(Car.prototype.arr)
//结果是 [100, 2, 3] [100, 2, 3]
prototype里面的内容
- construtor: 指向构造函数本身,相当于实例化函数后prototype里面的内容
- construtor是可以在原型里面更改的
Car.prototype实例化之后仍可修改
- 修改prototype会实时更新到所有实例化对象上
- 因为__proto__存的是prototype的引用
Car.prototype = {
constructor:function Car(){}
}
constructor:
constructor是构造器,在实例化对象的prototype下面。它指向构造函数本身,且下面存放的是构造函数原型的属性。(与当前最新的构造函数原型同步。)
原型考题
Car.prototype.name = 'Benz';
function Car(){}
var car = new Car();
Car.prototype = {
name:'Mazda';
}
console.log(car.name);
//'Benz'
//因为修改prototype的对象是在实例化之后修改的,修改的值,只修改了car的prototype的下面的constructor所指向的构造函数里面的prototype, 并未修改到实例化对象里面的prototype的值;
//输出结果
Car.prototype.name = 'Mazda';
function Car(){}
var car = new Car();
Car.prototype.name = 'Benz';
console.log(car.name);
//Benz // 对同个原型修改,所有对之前之后实例化的所有对象有效。
直接对原型修改与对原型重新赋值:
//因为实例化时,this拿的是prototype的地址引用,如果地址引用没变,修改里面的值是可以修改的。
//实例化之后,再给prototype赋值新对象,是一个新的地址.
//而car的this里面存的还是原来的prototype的引用,所以还是原来的值,'Benz';
window和return的不同:
window保存到全局。window.variable = fun;
return在函数执行完后,返回值,如果要全部保存的话,要用变量接收。var variable = test();
原型继承:
function TelePhone(){
}
TelePhone.prototype.name = '电话';;
function HandPhone(color, brand, system){
this.color = color;
this.brand = brand;
this.system = system;
}
HandPhone.prototype = {
constructor: TelePhone,
rom: '64G',
ram: '6G',
screen: '18:9',
call: function(){
console.log('I am calling somebody');
}
}
var hp1 = new HandPhone('black','iPhone','IOS');
console.log(HandPhone.prototype);
console.log(hp1.prototype.constructor.prototype); //能得到Teel的原型
构造函数与new的关系
构造函数必须经过new,才能生成原型属性;
因此,原型是存在于实例对象里边的,是实例对象的this下面的一个属性;
(如果不经过实例化,那么原型就没有载体)
Object.create()
- 自定义对象原型的创建对象的方法
- Object.create(原型对象或null)
- 创建没有原型的对象,Object.create(null);
function Obj(){
}
Obj.prototype.num = 1;
var obj1 = Object.create(Obj.prototype);
var obj2 = new Obj();
console.log(obj1);
console.log(obj2);
//两个对象的构成完全一样
Object.prototype.toString.call()
- [object Object]
- [什么类型的,什么构造函数]
- 用来判断类型
call/apply/bind
- 作用:改变this指向,调用里面的方法
- 不同之处:参数不同,call传一组对应的参数,apply传arguemnts, bind传参与call一样,但返回的是函数,需要再加个执行符号。
- 用法
- Car.call(car, 一组参数);
- Car.apply(car, [ar1, ar2]);
- Car.bind(car, 一组参数) ();
- test() 等于test.call()
function Car(brand, color){
this.brand = brand;
this.color = color;
}
var newCar = {};
Car.call(newCar, 'Benz', 'red');
console.log(newCar);
//newCar里面有了两个属性
//.call改变了this的指向,借用了属性和方法
call/apply用法案例
function Compute(){
this.plus = function(a, b){
console.log(a + b);
}
this.minus = function(a, b){
console.log(a - b);
}
}
function FullCompute(){
Compute.apply(this);
//Compute.call(this);
//Compute.bind(this)(); //再加个执行符号
this.mul = function(a, b){
console.log(a * b);
}
this.div = function(a, b){
console.log(a / b);
}
}
//借用以前函数的方法和属性,
//使用情景:
//维护以前的模块,补充完整模块
//多人协作
//分类
关于__proto__:
- 这是系统自动创建的,如果自己造的__proto__属性并赋值,不能构成原型链继承
- 无法向下访问proto里面的属性
公共原型
funtion Teacher(){
}
Teacher.prototype.name = 'teachers';
Student.prototype = Teacher.prototype;
function Student(){
}
//如此,当修改Student.prototype里面的属性时,Teacher的原型也修改了。
//二者公用同个原型对象。
圣杯模式 - 企业级的原型继承方法
- 使用中间的缓冲构造函数Buffer,隔离两个prototype
- 需求:继承Teacher.prototype, 且能够在Student.prototype里面写东西
function Teacher(){}
Teacher.prototype.major = '软件工程';
function Buffer(){}
Buffer.prototype = Teacher.prototype;
var buffer = new Buffer();
function Student(){}
Student.prototype = buffer; //student的原型是实例化的buffer对象。
Student.prototype.myFun = function(){}
//这样,就能够只继承目标的prototype,又能往自己的prototype里面写东西了。
- Teacher也叫做超类,指的是继承的谁。命名是super_class, 也叫继承源。
- 超类指的是原型继承中,被继承的类。如Teacher。
封装圣杯模式
- 分析,创建1个Buffer继承原型,并添加constructor和super_class
//圣杯模式,模块化的写法
var inherit = (function (Target, Origin){
var Buffer = function(){}
return function(){
Buffer.prototype = Origin.prototype;
Target.prototype = new Buffer();
Target.prototype.constructor = Target;//添加构造器指向自身
Target.prototype.super_class = Origin;//添加超类指向继承源
}
})();
function Teacher(){}
Teacher.prototype.name = '小野老师';
function Student(){}
inherit(Student, Teacher);
var s = new Student();
console.log(s);
//圣杯模式封装, 原型链继承-企业
function inherit(Target, Origin){
function Buffer(){}
Buffer.prototype = Origin.prototype;
Target.prototype = new Buffer();
Target.prototype.constructor = Target;//添加构造器指向自身
Target.prototype.super_class = Origin;//添加超类指向继承源
}
模块化开发:用自执行函数的方式,构建封闭的作用域,相当于有了自己的命名空间,用于多人协作开发,互不影响。防止了全局变量污染,利于维护和二次开发。
闭包的分类:1. 普通的闭包:即返回一个函数,或返回一个对象(对象里带着函数)。2.构造函数的闭包:隐式返回this。
普通的闭包特点:1. 变量私有化:产生了私有变量:返回的函数能够拥有父函数的变量。2. 保护数据:只有return出去的函数能访问。
构造函数闭包特点: new生成了this。然后隐式return this。