Js中的原型初识

188 阅读4分钟

原型

定义

原型其实就是( prototype ),是function对象的一个属性,console出来,它的结果也是对象.

示例代码
function Handphone (color,brand){
  this.color = color;
  this.brand = brand;
  this.screen = '18:9';
  this.system = 'Android';
}
	Handphone.prototype.rom = '64G';
	Handphone.prototype.ram = '6G';

	var hp1 = new Handphone('red','小米');
	console.log(hp1);
	console.log(hp1.rom);
	
结果

当使用了prototype之后,我们就能够访问到[ hp1.rom ]所以打印的 ' 64G '.这也就是说,

这个prototype是定义构造函数构造出来的每个对象的公共祖先.

所有该构造函数构造出的对象都可以继承原型上的属性和方法.

为了让开发的耦合度降低,其实所有的方法最后都会挂载到原型上去,例如我们上面的代码,screen和system就是写死了的,其实这样子是不好的,我们尝试改写一下:

Handphone.prototype.screen = '18 : 9';
Handphone.prototype.system = 'Android';
Handphone.prototype.rom = '64G';
Handphone.prototype.ram = '6G';

只有需要传参配置的部分,才会写在构造函数里,例如:

function Handphone (color,brand){
  this.color = color;
  this.brand = brand;
}

同时我们也可以在原型里增加方法:

Handphone.prototype.call = function(){
  console.log('我在打电话');
}
// 一样可以使用:
hp1.call();
// 访问到

同时

console.log(Handphone.prototype);

如果是这个样子打印的话,结果就会是{ constructor f },在这个阶段面,我们只需要记住,constructor指向的是构造函数本身.

这时

// 我们可以将handphone里的构造函数,重新指向一个新的函数,如下:
function Telephone(){}
Handphone.prototype = {
  constructor : Telephone
}

这个时候我们再打印出来,结果就会指向Telephone,而不是Handphone了.

额外总结

prototype其实是属于每一个实例化对象的,并不属于某一个构造函数.

所谓的"__ proto __:Car.prototype ",这种写法,其实是JS规定的,就是在强调这是源文件一样的东西,不要轻易的随便更改,我们其实可以将其看成键值对的形式来理解.

所以,__ proto __ ,就是每一个实例化对象的原型的容器,就是拿来装prototype的.

但是

prototype里面的属性也是可以更改的,例如:

function Person(){ }// 先定义一个函数
  person.prototype.name = '233';

var P1 = {
  name : 'www';
}

var person = new Person(); 	
	console.log (person.name); // 这里打印出来的就是233

person.__proto__ = P1;
// 在这里我们就将p1这个对象里面的属性,给赋值到proto里了,并且改写了name属性
console.log(person.name);//这里打印的就是改写过后的www

有关在原型中的继承

p.prototype.tSkill = 'java';
function p (){}
var P = new p();

T.prototype = P;
function T (){
  this.mSkill = 'JS';
}
var t = new T();

s.prototype = t;
function s(){
  this.pSkill = 'css';
}
var S = new s();

console.log(S.pSkill);

沿着proto这条线去找原型里的属性,一层一层去继承原型属性的这个链条,叫做原型链.

所以原型链的顶端,其实是Object.prototype

同时Object.prototype这个属性下,保存了toString ( ); 这个方法.

在原型链的继承里,下一级通常是无法往上增删改的,只能够查.但这也不是绝对的,例如:

p.prototype.tSkill = 'java';
function p (){}
var P = new p();

T.prototype = P;
function T (){
  this.mSkill = 'JS';
  this.sucess{
    a:'28';
    d:'30';
  }
}
var t = new T();

s.prototype = t;
function s(){
  this.pSkill = 'css';
} 
var S = new s();
S.sucess.b = '100'; 
// 这个样子写的话,最后就会将b这个属性,加入到sucess这个对象中去了.
console.log(S.pSkill);

所以说引用值是可以的,但是不推荐这样子去做.

create方法

语法格式 : Object.creat ( ); 括号里只能放对象或则是null,必须放东西,不放不行.

// 或者我们可以这样:
var test = {
  num:2
}

funtion Obj(){}
Obj.prototype.num = 1;
var obj1 = Object.create(Obj.prototype);
//var obj1 = Object.create(test);  也可以传入test对象.
var obj2 = new Obj(); 
// 上面这两种方式,结果其实都是一样的.
console.log(obj1);
console.log(obj2);

正是因为有了create方法,所以这里有一道面试题:

问,是不是所有的对象都继承于Object.prototype ?

//下面这种情况创建的对象,就不会继承于Object.prototype
var obj1 = Object.create(null);

那么我们可以自己加上proto这个属性吗,请看下面的代码:

var obj = Object.create(null);

obj.num = 1;

var obj1 = {
  count:2
}
obj.__proto__ = obj1;

console.log(obj.count);

结果最终为

undefined

这就是说,只有系统加上的proto能正常的执行,如果是我们自己添加的,则不会生效.

( 系统自带的proto属性在console控制台的显示颜色偏浅,我们自己加的偏深. )