JavaScript——对象属性、创建对象、继承、原型链

一、对象

1. 属性类型

有两种属性:数据属性、访问器属性

用两对方括号括起来的特征值是内部值,不能直接访问

  1. 数据属性:包含一个数据值,可以进行读取和写入,有四个特征来描述它的行为:

    • [[Configurable]]:能否进行修改属性的特性、通过delete进行删除属性(默认值:true)
    • [[Enumberable]]:能否通过for-in循环返回属性(默认值:true)
    • [[Writable]]:能否修改属性的值(默认值:true)
    • [[value]]:包含属性的数据值(默认值:undefined)
  2. 访问器属性:getter、setter函数非严格模式下非必需,严格模式下,缺少会抛出错误。

    • getter函数:读取访问器属性且返回有效的值
    • setter函数:写入访问器属性,调入时传入新值,负责决定如何处理函数。
    • 同样有四个特征:

      • [[Configurable]]:能否进行修改属性的特性、通过delete进行删除属性(默认值:true)
      • [[Enumberable]]:能否通过for-in循环返回属性(默认值:true)
      • [[get]]:在读取属性时调用的函数(默认值:undefined)
      • [[set]]:在写入属性时调用的函数(默认值:undefined)

      当属性名前有_记号时,表示该属性只能通过对象方法访问

2. 方法:

  1. Object.defineProperty(属性所在的对象,属性名,修改该属性内容):修改属性默认的特征。
    Object.defineProperty(person,"name",{writable:false,value:"Nicholas"});

    当设置为不可修改后强行进行修改时,在非严格模式下,修改操作被忽略;在严格模式下,会导致抛出错误

  2. Object.defineProperties(属性所在的对象,修改内容):可以一次定义多个属性。

    var book{};
    Object.defineProperties(book,
    {
        _year:{value:2004},
        edition:{value:1},
        year:{
            get:function(){
                return this.year;
            }
            set:function(newvalue){
                if(newvalue>2005)
                {
                    this._year=newvalue;
                    edition+=newvalue-2004;
                }
            }
        }
    }
    );
  3. Object.getOwnPropertyDescripor(属性所在的对象,所要读取特征值的属性名):可以取得给定属性的特征值,返回值是一个对象。

    var descriptor=Object.getOwnPropertyDescripor(book,"_year");
    alert(descriptor.value);//2004  

二、创建对象

1.工厂模式

用函数来封装以特定接口来创建对象

funtion creatperson(name,age){
    var o=new Object();
    o.name=name;
    o.age=age;
    o.sayname=function(){
        alert(this.name);
    };
    return o;
}
var person=creatperson("rose,"18);

缺点:无法得知创建的对象的类型。

2.构造函数模式

可以创建自定义构造函数,从而定义自定义对象类型的属性和方法
构造函数名第一个字母大写

funtion Person(name,age){
    this.name=name;
    this.age=age;
    this.sayname=function(){
        alert(this.name);
    };
}
var person=new Person("rose,"18);
//将构造函数的作用域赋值给新对象(因此this就指向了这个新对象)
//返回新对象

缺点:每个方法都要在创建对象的时候重新创建一边,没有实现共享。

3.原型模式(重点)

  1. 对象

    • 每个函数都有一个prototype原型属性,是一个指针,指向函数的原型对象,该对象包含所有实例共享的属性和方法

      function Person(){}
      Person.prototype={
          constructor:Person,//因为重写了,所以需要特意设置
          name:"rose",
          age:18,
          sayname:function(){
              alert(this.name);
          }
      };//这种定义方式是对象字面量,重写了原型对象
      var person1=new Person();
      var person2=new Person();
      alert(person1.sayname==person2.sayname);//true
      //因为Person.prototype中的内容是共享的,所以相同。
    • 原型对象自动获得constructor构造函数属性,也是一个指针,指向prototype所在de函数。
      eg:Person.prototype.constructor->Person

    • 当调用构造函数创建一个实例后,该实例将包含一个指针属性[[Prototype]],指向构造函数的原型对象

    • 当执行一个对象的某个属性时,会先在实例中搜索该属性名,如若没有,则会在原型对象中搜索。
    • 当在实例中为对象添加一个属性时,该属性会覆盖住原型对象中的同名属性,如若delete掉该属性,则显示原型对象中的。
      function Person(){};
      Person.prototype.name="rose";
      person1=new Person();
      person1.name="jack";
      alert(person1.name);//"jack"
      delete person1.name;
      alert(person1.name);//"rose"
  2. 方法

    • isPrototypeOf(对象):如果[[Prototype]]指向构造函数的原型对象,即Person.prototype,则返回true。
    • Object.getPrototypeOf(对象):返回[[Prototype]]的值。
    • hasOwnProperty(name):在实例中返回true。
    alert(Person.prototype.isPrototypeOf(person1));//true
    alert(Object.getPrototypeOf(person1)==Person.prototype);//true
    alert(Object.getPrototypeOf(person1).name);//"rose"
  3. 原型与in操作符

    • 使用方式:单独使用和在for-in循环中使用。

      • 单独使用:对象能够访问给定属性时返回true,即属性存在于对象(实例和原型对象)中
        继续上例,alert("name" in person1);//true

        下述函数可以用来确定属性是存在于实例还是原型对象

        function hasPrototypeProperty(object,name){
            return !object.hasOwnProperty(name)&&(name in object);
        }
        alert(hasPrototypeProperty(person,"name"))
        //在原型对象中返回true,在实例中返回false
      • for-in循环:返回的是所有能够通过对象访问的、可枚举的属性。
        将[[Enumberable]]设置为false的实例属性也会返回,因为规定所有开发人员定义的属性都是可枚举的。

        Object,keys(对象)方法:返回一个包含所有可枚举属性的字符串数组

        var keys=Object.keys(person1);
        alert(keys);//"name,age"
  4. 动态性:当在创建一个实例后,如果再全部重写对象原型,则会切断现有原型和之前所有对象实例的联系,即实例引用的是未重写的原型。

缺点:在原型对象中定义的属性方法都是共享的,对于引用类型值的属性,问题比较大。

4.组合使用构造函数和原型模式(最常见)

构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性,节省了内存。

5.动态原型模式

所有信息都封装在了构造函数中,在其中可以判断某个属性是有效,来决定是否初始化原型。

function Person(name)
{
    this.name=name;
    if(typeof this.sayname!="function"){
        Person.prototype.sayname=function(){
            alert(this.name);
        };
    }
}

6.寄生构造函数模式(不推荐使用)

创建一个函数,来封装创建对象的代码,再返回新创建的对象,很像构造函数。

7.稳妥构造函数模式(适合安全环境)

实例方法不引用this和不使用new操作符调用构造函数,其余地方和寄生构造函数模式相同。
与上述代码不同的地方:alert(name);

三、继承

只支持实现继承,则继承实际的方法,且依靠原型链

1.原型链

  • 再次讲一下 原型对象、构造函数、实例的关系:
    每个构造函数都有一个prototype内部指针属性,该属性指向原型对象,原型对象中有一个constructor指针属性,指向构造函数,创建一个实例调用该类型的构造函数,有了构造函数所定义的属性和方法,同时自动有一个内部指针属性[[Prototype]],指向原型对象。
  • 让一个原型对象等于另一个类型的实例,此时这个原型对象中包含着指向另一个原型对象的指针,另一个原型对象包含指向另一个构造函数的指针,假如另一个原型又是另一个类型的实例,就构成了实例与原型的链条,即原型链。
function A(){
    this.name="rose";
}
A.prototype.getname=function(){
    return this.name;
};
function B(){
    this.age="18";
}
B.prototype=new A();
B.prototype.getage=function(){
    return this.age;
};
var person=new B();
alert(person.getname());//"rose"
//在这个例子中,调用person.getname();会先搜索实例,然后B.prototype,然后A.prototype.
//应用原型链进行操作是,就是一层一层的搜索,直至找到或者到原型链末端
  • 所有函数的默认类型都是Object的实例,所以原型链的末端应该是Object,所以上例中,A.prototype=new Object();
  • 上例中person可以是A、B、Object的实例。
  • 给原型添加方法的代码一定要放在继承上一层原型的语句之后。

缺点:
* 原先不想被共享的属性定义在构造函数中,但是原型链中,实例是下一层的原型对象,不想被共享的属性变成共享的了。
* 在继承创建实例时不能传递参数。所以不建议单独使用原型链。

2.借用构造函数

  • 在子类型构造函数的内部调用超类型构造函数。这样只会在创建B实例的环境下调用A的构造函数,这样就可以每个实例有属于自己的A,B构造函数所定义的的属性。
function B(){
    A.call(this);
}
  • 也可以继承的时候传递参数。
function A(name){
    this.name=name;
}
function B(){
    A.call(this,"rose");
    this.age=18;
}
var person=new B();
alert(person.name;//"rose"

缺点:构造函数模式存在的问题,没有函数共享、复用,所以也不建议单独使用。

3.组合继承(最常用的继承模式)

  • 使用原型链实现对原型属性和方法的继承,通过构造函数来实现实例属性的继承。
function A(name){
    this.name=name;
    this.colors=["red","blue","green"];
}
A.prototype.getname=function(){
    alert(this.name);
};
function B(name,age){
    A.call(this,name);
    this.age=age;
}
B.prototype=new A();
B.prototype.constructor=A;
B.prototype.getage=function(){
    alert(this.age);
};
var person1=new B("rose",18);
person1.colors.push("black");
alert(person1.colors);//"red,blue,green,black"
person1.getname();//"rose"
var person2=new B("jack",19);
alert(person2.colors);//"red,blue,green"
person2.getage();//19

4.原型式继承

  • 基于原有的对象创建新对象,即创建原有对象的副本。
function object(o){
    function F(){}
    F.prototype=o;
    return new F();
}
var person={name:"rose",colors=["red","green"]};
var person1=object(person);//创建person的一个副本
person1.name="jack";//根据需求对得到的对象加以修改
  • Object.creat(对象,修改添加内容);当只有第一个参数时,等同于object()。
    var person1=Object.creat(person,{name:{value:"jack"}});

5.寄生式继承

  • 创建一个封装函数,内部进行继承,和修改添加属于对象自己的属性和方法,再返回这个对象。
function creat(person){
    var person1=object(person);
    person1.sayhi=function(){
        alert("hi");
    };
    return person1;
}
var person1=creat(person);
person1.sayhi();//"hi"

6.寄生组合式继承

  • 通过构造函数来继承属性,原型链来继承方法。
  • 本质上就是使用寄生式继承来继承原型,然后再将结果指定给子类型的原型
function inherit(B,A){
    var prototype=object(A.prototype);//创建对象,即new A()
    prototype.constructor=B;//增强对象
    B.prototype=prototype;//指定对象
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值