一、原型prototype
- 原型prototype它是function对象上的的一个属性
- 把它打印出来会发现它是一个对象
每一个函数都有prototype,在prototype上有一个constructor属性,存储的是当前构造函数本身
// 原型:prototype是function对象的一个属性// 将其打印出来的时候我们会发现它是一个对象// 每一个函数都有prototype,在prototype上有一个constructor属性,存储的是当前构造函数本身function Handelphone(){}console.log(Handelphone.prototype);
原型prototype是定义构造函数构造出的实例对象的公共祖先
- 所有被该构造函数构造出的实例对象都会继承原型上的属性和方法
实例化的对象在寻找属性或者方法的时候,首先看自己的构造函数上有没有,有的话,就优先使用自己的,没有会基于proto向原型上查找
// 原型prototype是定义构造函数构造出的实例对象的公共祖先// 所有被该构造函数构造出的实例对象都会继承原型上的属性和方法// 实例化的对象在寻找属性或者方法的时候,首先看自己的构造函数上有没有,有的话,就优先使用自己的,没有会基于__proto__向原型上查找function Handelphone(color, brand){this.color = color;this.brand = brand;this.screen = '18:9';this.system = 'Android';}Handelphone.prototype.rom = '64G';Handelphone.prototype.ram = '6G';Handelphone.prototype.screen = '16:9';var hp1 = new Handelphone('red', '小米');var hp2 = new Handelphone('black', '华为');console.log(hp1.ram);console.log(hp2.rom);console.log(hp1.screen);console.log(hp2.screen);
上面的代码优化
在封装插件化的时候,一般都是将方法写在原型上,有固定值的属性也要写在原型上;只有需要传递参数的配置项才写在构造函数上
// 代码优化一// 在封装插件化的时候,一般都是将方法写在原型上,有固定值的属性也要写在原型上;只有需要传递参数的配置项才写在构造函数上function Handelphone(color, brand, system){this.color = color;this.brand = brand;this.system = system;}Handelphone.prototype.screen = '18:9';Handelphone.prototype.rom = '64G';Handelphone.prototype.ram = '6G';Handelphone.prototype.call = function(){console.log('I am calling somebody');}var hp1 = new Handelphone('red', '小米', 'Android');var hp2 = new Handelphone('black', 'iPone', 'IOS');console.log(hp1.ram);console.log(hp2.rom);console.log(hp1.screen);console.log(hp2.screen);// 代码继续优化function Handelphone(color, brand, system){this.color = color;this.brand = brand;this.system = system;}Handelphone.prototype = {ram: '6G',rom: '64G',screen: '18:9',call: function(){console.log('I am calling somebody');}}var hp1 = new Handelphone('red', '小米', 'Android');var hp2 = new Handelphone('black', 'iPone', 'IOS');console.log(hp1.ram);console.log(hp2.rom);console.log(hp1.screen);console.log(hp2.screen);
二、实例化对象无法增删改原型上的属性和方法
实例化的对象是无法增删改原型上的方法,但是可以查询到原型上的方法
// 实例化的对象是无法增删改原型上的方法,但是可以查询到原型上的方法function Test(){}Test.prototype.name = '我是prototype上的属性';var test = new Test();// 实例化对象可以访问原型上的属性或者是方法console.log(Test.prototype);console.log(test.name);// 实例化对象无法向原型上增加属性或者是方法test.num = 1;console.log(Test.prototype, test);// 实例化对象本身无法删除原型上的方法delete test.name;console.log(Test.prototype, test);// 实例化对象本身无法修改原型上的属性或者是方法test.name = 'proto';console.log(Test.prototype, test);
三、函数原型上的constructor属性
每一个函数都有一个prototype属性,在prototype上有一个constructor属性,它指向构造器,也就是构造函数本身
构造函数原型上的constructor属性的指向是可以更改的
// 每一个函数都有一个原型属性「prototype」// 函数的原型上「prototype」有一个constructor属性,它指向的是构造器,也就是构造函数本身// 函数原型上的constructor属性的指向是可以更改的function Telephone(){}function Handphone(color, brand, system){this.color = color;this.brand = brand;this.system = system;}Handphone.prototype = {constructor: Telephone,screen: '5.7'}console.log(Handphone.prototype);console.log(Handphone.prototype.constructor);
四、实例化对象上的proto属性
只有实例化的对象才会有proto属性,不实例化的对象是没有该属性的
- proto属于每一个实例化对象,而不属于构造函数本身
proto其实就是每个实例化对象存储原型prototype的容器而已
// 只有实例化的对象才有__proto__属性,没有实例化的对象是没有该属性的// __proto__属于的是每一个实例化对象本身,而不属于构造函数本身// __proto__属性其实就是每一个实例化对象用来存储原型prototype属性的一个容器function Car(){// 隐式var this = {__proto__: Car.prototype;}// this.name = 'Mazda'}Car.prototype.name = 'Benz';var car = new Car();console.log(car.name);
五、原型上的proto属性的指向可以更改
原型prototype上的proto属性也是可以更改其指向的
// 原型上的__proto__属性也是可以更改其指向的function Person(){}Person.prototype.name = '张三';var p1 = {name: '李四'}console.log(Person.prototype.constructor);Person.prototype.constructor = p1;console.log(Person.prototype.constructor);
六、实例化之前和之后
```javascript
function Car(){}Car.prototype.name = 'Mazda';var car = new Car();Car.prototype.name = 'Benz';console.log(car.name);
function Car(){}Car.prototype.name = 'Mazda';var car = new Car();console.log(car.name);Car.prototype.name = 'Benz';
- 每一个函数都有一个原型prototype属性,原型上有一个constructor属性,它指向的是构造器,也就是当前这个构造函数本身- 在实例化对象之后再去更改原型上的属性和方法,并不会对实例化之前的对象产生影响```javascriptCar.prototype.name = 'Benz';function Car(){}var car = new Car();// 在实例化对象之后再去更改原型上的属性和方法,并不会对实例化之前的对象产生影响Car.prototype = {name: 'Mazda'}console.log(car.name);
七、window和return的问题
function func(){window.b = 10;}func();console.log(b);// window和return的问题function test(){var a = 1;function add1(){a++;console.log(a);}return add1;}var add = test();add();add();add();// 通过window的方式来实现上面的test函数function compute(){var a = 1;function add(){a++;console.log(a);}window.add = add;}compute();add();add();add();// 通过自执行函数的方式来实现compute函数var add = (function(){var a = 1;function add(){a++;console.log(a);}return add;})();add();add();add();;(function(){var a = 1;function add(){a++;console.log(a);}window.add = add;})()add();add();add();
八、插件化
- 用自执行函数来写插件,是为了防止全局变量污染
- 里面写构造函数是为了方便构造出新的实例化对象
最后用window.xxx是为了将该构造函数抛到全局去,方便使用
// 插件化// 用自执行函数是为了防止全局变量污染// 用构造函数是为了方便构造实例化对象;(function(){function Test(){}Test.prototype = {}window.Test = Test;})()var test = new Test();
九、作业
写一个插件,调用该方法即可实现加减乘除
// 作业// 写一个插件,调用该方法即可实现加减乘除;(function(){var Compute = function(opt){this.x = opt.firstNum;this.y = opt.secondNum;}Compute.prototype = {add: function(){return this.x + this.y;},minus: function(){return this.x - this.y;},mul: function(){return this.x * this.y;},div: function(){return this.x / this.y;}}window.Compute = Compute;})();var compute = new Compute({firstNum: 8,secondNum: 2});console.log(compute.add());console.log(compute.minus());console.log(compute.mul());console.log(compute.div());
// 写一个插件,调用该方法即可实现加减乘除;(function(){var compute = function(){};function Compute(){}Compute.prototype = {add: function(a, b){return a + b;},minus: function(a, b){return a - b;},mul: function(a, b){return a * b;},div: function(a, b){return a / b;}}window.Compute = Compute;})();var compute = new Compute();console.log(compute.add(10, 5));console.log(compute.minus(10, 5));console.log(compute.mul(10, 5));console.log(compute.div(10, 5));
