您的位置:首页 > Web前端 > JavaScript

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

2017-07-14 17:21 162 查看
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;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  javascript