1. 认识原型
1.1.1 原型的概念
在说原型之前,我们先创建一个手机的构造函数
function HandPhone(color,brand){
this.color = color;
this.brand = brand;
this.screen = '18:9';
this.system = 'Android';
}
下面,我们打印一下该构造函数的prototype属性,看看是什么
console.log(HandPhone.prototype);//{constructor: ƒ}
原型prototype其实是function对象的一个属性,打印出来看了一下,结果它也是一个对象
我们可以往该原型身上添加属性和方法
HandPhone.prototype.rom = '64G';
HandPhone.prototype.ram = '8G';
HandPhone.prototype.screen = '16:9';
HandPhone.prototype.call = function(){
console.log('手机可以打电话');
}
实例化对象
var hp1 = new HandPhone('red','小米');
var hp2 = new HandPhone('white','iphone');
console.log(hp1.rom); // 可以访问原型上的属性
console.log(hp2.ram); // 可以访问原型上的属性
console.log(hp1.screen);// 自己this身上有的,不会去原型身上找
hp2.call()
这个prototype 是定义构造函数构造出来的每个对象的公共祖先
所有被该构造函数构造出来的对象都可以继承原型上的属性和方法
在写插件的时候,往往需要通过传参进行配置的配置项,会写到构造函数内部,
而固定的方法和拥有固定值的属性,会放到原型上继承
这样的好处是在于,每次new的时候,代码不会冗余
1.2.1 原型的增删改查
创建一个test的构造函数
function Test(){
}
Test.prototype.name = 'prototype'
var test = new Test();
console.log(test.name);// 可以访问
test.age = 18;// 给自身的this身上添加了age属性,prototype上没有
delete test.name ; // 删除的也是this上的属性,不影响prototype
test.name = 'proto' ;// 也无法修改prototype
总结: 原型可以访问,但是不能通过实例化对象进行修改增加和删除
1.3.1 优化案例
因为原型是一个对象,所以可以通过对象的形式来优化代码
在插件化开发中,也是使用对象的形式来处理,而不是一个一个添加属性或者方法
function HandPhone(color,brand){
this.color = color;
this.brand = brand;
this.screen = '18:9';
this.system = 'Android';
}
// 优化此处的代码
HandPhone.prototype.rom = '64G';
HandPhone.prototype.ram = '8G';
HandPhone.prototype.screen = '16:9';
HandPhone.prototype.call = function(){
console.log('手机可以打电话');
}
// 优化后
HandPhone.prototype = {
rom:'64G',
ram:'8G',
screen:'16:9',
call:function(){
console.log('手机可以打电话');
}
}
var hp1 = new HandPhone('red','小米');
var hp2 = new HandPhone('white','iphone');
1.4.1 constructor
constructor是一个函数,指向构造器本身。
function TelPhone(){}
function HandPhone(color,brand,system){
this.color = color;
this.brand = brand;
this.system = 'Android';
}
// 通过修改构造器原型上的constructor,可以硬指定构造器
HandPhone.prototype = {
constructor:TelPhone
}
var hp1 = new HandPhone('white','iphone','IOS');
console.log(hp1.constructor);// 是一个函数,指向构造器本身 ƒ TelPhone(){}
console.log(HandPhone.prototype.constructor);// 是一个函数,指向构造器本身 ƒ TelPhone(){}
1.5.1 proto
声明一个构造函数
function Car(){
}
Car.prototype.name = 'Benz';
var car = new Car();
console.log(car);// Car {}
发现打印出来的结果是一个空的对象
console.log(car.name);//Benz
打印name却可以打印出来
相当于,在实例化的时候,在构造函数内部,隐式声明了一个this
当实例访问name属性的时候,自身的this身上没有
就会通过访问实例化对象身上独有的__proto__属性,
这个属性所对应的值,就是构造器原型上的值
也就是说,__proto__只有实例化对象身上才有,在实例化之前,是访问不到的
因为都没有this
所以,我们才需要通过把一些将来需要用到的属性挂靠到构造器的原型身上
等待实例化对象生成this,通过内置__proto__属性来访问原型上的属性和方法
自身没有对应属性的时候,需要有个门牌号来访问prototype上的属性,
所以就起了个名字叫做__proto__,所以也可以将它看成是存放prototype的一个容器
因为要访问prototype,需要有个键名,所以JS自动加上__proto__
function Car(){
var this = {
自身没有name,所以访问__proto__
__proto__ : Car.prototype
}
}
*/
1.5.2 小案例
function Car(){};
Car.prototype.name = 'Benz';
var car = new Car();
Car.prototype = {
name:'Baoma'
};
console.log(car.name);
先声明一个构造函数,并在原型上添加name属性,在实例化之后,再修改原型上的name属性,最终打印出来的值是多少呢?
答案是:Benz
执行过程
只有实例化之后,才会生成this,才会往容器中添加原型上的值
实例化之后,再更改原型,不影响原本的实例化对象,因为没有再次实例化
function Car(){
var this = {
__proto__:Car.prototype = {
name:'Benz'
}
}
}
1.6.1 结合立即执行函数来开发加减乘除简单插件
先来看一下步骤是什么
插件化开发传入任意两个数,可以调用方法实现加减乘除
1.使用立即执行函数隔离作用域
2.写构造函数,在原型上挂载方法
3.将构造函数暴露给全局
4.实例化对象,传入配置项
先把插件写好,options是配置项,写好了之后,需要使用该插件的时候,只需要在new的时候传入配置项即可
;(function(){
function Test(options){
this.firstNumber = options.firstNumber;
this.secondNumber = options.secondNumber;
};
Test.prototype = {
add:function(){
return this.firstNumber + this.secondNumber
},
reduce:function(){
return this.firstNumber - this.secondNumber
},
multiply:function(){
return this.firstNumber * this.secondNumber
},
except:function(){
return parseInt(this.firstNumber / this.secondNumber)
}
}
window.Test = Test
})()
使用插件
var options = {
firstNumber:5,
secondNumber:3
}
var test = new Test(options);
console.log(test.add());
console.log(test.reduce());
console.log(test.multiply());
console.log(test.except());