迷迷糊糊很多年的js原型链

1.属性描述器(属性标签)
对象的每个属性都有多个标签,也叫属性描述器
writable  是否可写
enumerable 是否可枚举
configurable 是否可配置删除
value 属性值
get方法\set方法 


get与set是对象内部的关键字,他们与其他属性不同,并非键值对形式存在,可以直接咦get set关键字开头后面跟函数表达式,其默认方法如下
get方法:get 属性名(){return this.$属性名}

set方法:set 属性名(val){this.$属性名=val}

var student={
     name:"小明",
     get age(){
         if(this.$age){
             return this.$age;
         }else{
	     return new Date().getFullYear()-1988;
         }
     },
     set age(val){
         this.$age=val-0;
     }
}
console.log(student.age);=>29
student.age=100;
console.log(student.age);=>100

另外for...in  循环遍历对象属性,会把原型链上的属性也遍历出来,除非该属性描述器enumerable=false

for( obj in Object){ ...}
需要注意的是:遍历顺序不确定;对象的属性描述器enumerable为false时将不会被枚举;受对象原型链影响;

JS delete运算符:
对象属性的描述器 configurable=false时,该属性不能被delete,return false;全局变量、局部变量都不可delete,隐式声明全局变量可以被delete;Object.prototype不可被delete;delete对象不存在的属性时 依然会返回true




通常代码中自定义创建对象的属性的标签默认值都为true,继承自Object的有些属性的标签为false,可通过以下方法来查看:
以标签enumerable为例,其他标签用法一样
var cat={ leg:4}
cat.propertyIsEnumerable("leg")=>true 可以枚举
cat.propertyIsEnumerable("toString")=>false  不可枚举
如果修改属性标签,可通过以下方法
Object.defineProperty(对象名,属性名,{enumerable:boolean值,value:"属性值",get:function(){},set:function(){}})
Object.defineProperty(cat,"leg",{enumerable:false,value:"4"});
再查看
cat.propertyIsEnumerable("leg")=>false 不可枚举


也可以通过以下方法来列举对象某一属性的所有标签
Object.getOwnPropertyDescriptor(对象名,属性名);
Object.getOwnPropertyDescriptor({age:12},"age")=>
Object{writable:true,enumerable:true,configurable:true,value:12}

2.对象原型链(有点难度)
创建对象:new 对象构造器(),Object.create(对象原型)


I.new创建的对象的原型_proto_指向其构造器的prototype属性,而这个属性也是一个对象,而prototype的原型指向Object.prototype属性,同样它也是一个对象,它的原型指向null,所以Object是所有原型链的末端。当函数声明创建一个函数的时候,它会预设一个prototype属性,如下

var foo=function(){this.x=1}

foo.prototype=>{constructor:foo,_proto_:Object.prototype,x:1}
只有函数对象会预设prototype属性,Object对象也有,其他对象没有


II.自定义对象的原型_proto_直接指向Object的prototype属性

var foo=function(){}
foo.prototype.y=1;
var obj=new foo();
obj.x=2;
console.log(obj.x)//2  因为obj对象本身含有x属性,没啥说的
console.log(obj.y)//1  obj对象属性没有y属性,会去他的原型_proto_即其构造器的prototype属性中找(foo.prototype),如果还没有则继续上寻,直到原型链末端object,仍然没有则返回undefined;

如果需要判断属性是否属于本对象, 可调用hasOwnProperty方法判断,如果属性不在对象本身,来自对象的原型链,则同样会返回false

obj.hasOwnProperty("x")=>true
obj.hasOwnProperty("y")=>false
foo.hasOwnProperty("y")=>false
foo.prototype.hasOwnProperty("y")=>false

如上述原型链:
obj._proto_=>foo.prototype
foo.prototype._proto_=>Object.prototype
Object.prototype._proto_=null
但不是所有对象都有_proto_属性,如var obj=Object.create(null),此时的obj._proto_为undefined.


III.Object.create(参数)将参数作为新对象的原型

var a={x:1}
var obj=Object.create(a);//此时obj._proto_指向对象a,而a的原型a._proto=>Object.prototype
obj.hasOwnProperty("x");//false
a.hasOwnProperty("x");//true


综上所述:js中的对象与生俱来都拥有许多方法,皆是继承自原型链末端的Object对象


3.谈过原型链,我觉得是时候谈谈Js中的继承了,下例中定义一个父类Person,一个子类Student
父类Person包含属性name\age\legs\head,以及行为(方法)hi\walk,其中属性legs和head是固定的

子类Student除包含父类属性和行为还包括className,以及study行为,还重写了父类的hi行为

function Person(name,age){
	//父类的属性name、age,此时的this是指向函数的调用对象
	this.name=name;
	this.age=age;
}
//父类的hi行为
Person.prototype.hi=function(){
	console.log("大家好,我是"+this.name+",我今年"+this.age+"岁了!");
}
//父类walk行为
Person.prototype.walk=function(){
	console.log(this.name+"正在散步呢...");
}
//父类常有不变的属性
Person.prototype.legs=2;
Person.prototype.head=1;


function Student(name,age,className){
	//调用父类,并将现在的调用对象传递给父类,这句代码只是为了更好的体现继承,可直接用this.name=name;this.age=age,只是不能提现继承
	Person.call(this,name,age);
	//子类Student扩展的属性
	this.className=className;
}
/*下面这句是重点 如果要将Student继承Person  则必须让Student.prototype在原型链上指向Person.prototype即Student.prototype._proto_=Person.prototype,下句就是执行了此操作,当然不是唯一方法,通过Student.prototype=new Person();同样可完成此操作。此处有几个误区:
a.  Student=new Person();//这样不是继承Person,而是通过化Person对象直接赋值给Student,并覆盖了原来的Student对象
b.  Student.prototype=Person.prototype;//这样也不是继承,而是直接将Person.prototype赋值给了Student.prototype,这样他们的原型都指向Object.prototype,即Student.prototype._proto_=Person.prototype._proto_=Object.prototype,父类都是Object,而且在对Student进行扩展时,同样会修改掉Person*/

Student.prototype=Object.create(Person.prototype);
//此句代码没有实际意义,只是为了保证Student.prototype.constructor与当前对象的一致性,因为在执行完上句代码后Student.prototype.constructor=Person,如果不修改也不会影响继承。
Student.prototype.constructor=Student;


Student.prototype.study=function(){
	console.log(this.name+"在学习");
}
Student.prototype.hi=function(){
	console.log("大家好,我是来自"+this.className+"班的"+this.name+",我今年"+this.age+"岁了!")
}
var tom=new Student("tom","20","3");
tom.hi(); //大家好, 我是来自3班的tom, 我今年20岁了!
tom.walk();//tom正在散步呢...
tom.study();//tom在学习


注意:object.create()是ES5才有的继承父类的原型,不支持ie9以下的版本,可以使用以下代码来兼容IE9以下版本

if(!Object.create){
	Object.create=function(_proto_){
		function F(){};
		F.prototype=_proto_;
		return new F;
	}
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值