js---原型,原型链

1.原型---Person.prototype,在函数Person定义的时候就产生了,是所有由Person构造函数构造出的对象的祖先。祖先意味着子代可以继承他的属性。而这个Person.prototype等待着new时将自己放到this的__proto__中。

function Person(){
  //var this={__proto__:Person.prototype}
  ....//中间的代码
  // return this;
}
console.log(Person.prototype);//{constructor:Person(){},__proto__:Object}

2.原型链---就是在new Person()的时候,产生的this的同时,系统会给this配置一个属性__proto__,值是当前构造函数的原型即Person.prototype,通过原型的__proto__可以构成原型链。Person.prototype的__proto__指向的是Object,就构成了有两个原型的原型链,也就是person在Person.prototype找不到,可以到Object.prototype里找。

一切原型链的终端都是Object:

例如:

function Person(){
  //var this={__proto__:Person.prototype}
  // return this;
}
var person = new Person();
console.log(person.toString());//[object Object]  
//事实上在Person.prototype里没有这个toString方法,可以person依然可以调用,因为这个方法在Object.prototype上

3.原型系统默认会配属性:

constructor---指向当前原型是谁(哪个构造函数)的原型

__proto__----指向原型链中上一级的原型对象,可以被修改,修改后顺着__proto__找

例如:

function Person(){}
Person.prototype.name='ren';
function Student(){
  this.name='xuesheng'
}
var person = new Person();
Person.prototype = new Student();
console.log(person.name);//ren  为啥这里改了没用?因为你先new出来person,此时person的__proto__已经被Person.prototype赋值了。
//就是说__proto__里存的是Person.prototype的地址,你后来又给Person.prototype这个索引名字换了一个地址当然没作用
//你要是直接给person.__proto__换对象就会变,详情可以看原始型和引用型那章博客
var person1=new Person();
console.log(person1.name);//xuesheng 原型变成了student再new的
Person.prototype.name='lin';
console.log(person.name);//'ren' 修改新原型(student)的name,原先的原型不会改变
var person2 = new Person();
console.log(person2.name);//'lin' 此原型已经变了,再new就是遵循新的原型,而新的原型的name刚刚被改成了'lin'


4.不是所有对象都有原型的,比如由Object.create(null)出的对象,是没有原型的。

Object.create(原型)就是创建以什么为原型的对象。

例如:

var obj = {name:'Lin',age:12,sex:'female'};
var obj1 = Object.create(obj);
console.log(obj1,obj1.name);//{__proto__:{age:12,name:'Lin',sex:'female'}}  "Lin" 本身没有但是通过原型链找到父级有name属性
var obj2 = Object.create(null);
console.log(obj2);//{} 什么属性都没有的空对象
obj2.__proto__= obj //如果人为加__proto__是没有用的,系统默认产生的属性__proto__可以改,但是没有就是没有,人为加了也不会产生原型链的效果
console.log(obj2.name);//undefined
Object.create(原型,是否为可配置);
例子---啥叫可配置:凡是经过var声明出来的都是不可配置的,不能delete的
//在全局声明变量,相当于window.num = 123
var num = 123;
delete num;
console.log(num);//123 并没删掉--不可配置属性
var obj={name:'lin'}
delete obj.name;
console.log(obj.name);//undefined 删掉了--属于可配置的属性

5.赋值,正常来说子代只能获得父代的值,不能添加修改删除

但是,如果父代上时引用类型的值,可以通过子代改

function Father(){
    this.fortune={
        card1:'vasas'
    }
    this.name = 'heel';
    this.hel= 'haha'
}
function Son(){}
var father = new Father();
Son.prototype = father;
var son = new Son();
console.log(son.fortune.card1,son.name);//'vasas' 'heel'取到原型上的值
son.fortune.card1=2;
son.name=1;
console.log(father.fortune.card1,father.name,son.name);//2 'heel' 1 原型上原始值没有被修改,引用类型值被修改,自己身上添加了新属性
//想修改原型中的原始值,唯一方法就要在原型上操作
Son.prototype.hel='shen';
Son.prototype.name='haha';
console.log(father.hel,son.hel,father.name,son.name);//shen shen haha 1自己身上有就不会去原型身上找

6.原型链上常有名字相同的函数名,在执行的时候采取就近原则,找到就停止寻找,这是一种函数重写的方式。当然如果想要用某个原型上的方法,而不是默认的就近原则,可以用call/apply

var num=123,arr=[1,2,3],bol=true,str='12';
console.log(num.toString(),arr.toString(),bol.toString(),str.toString());//'123' '1,2,3' 'true' '12'
//还记得我们前面说的object.prototype上面的toString()出来的结果是类似于[Object XXX]的形式
//那么这几个为什么调用toString()之后都是字符串,证明Number.prototype,Array.prototype,Boolean.prototype,String.prototype上有toString函数的重写
//当然,如果你需要的时候也可以在原型链上采用重写的方式

//另外 document.write({})----[Object Object]说明是系统隐式调用了toString方法转换再输出
//所以undefined null 自己构造的没有原型的对象 都不能调用toSting() 也不能用document.write()方式打印 



猜你喜欢

转载自blog.csdn.net/github_39132847/article/details/79668013