Javascript类与对象

Javascript中本身没有“类”,“实例”,“继承”的概念

可通过new Object()json方式创建一个对象

下面介绍

  • 使用原型和构造函数模拟类,创建对象
  • 使用原型链和伪装函数组合模拟继承,创建子对象

使用function,prototypeconstructor模拟 (参考Javascript继承机制的设计思想

  • function Xxx(args){...}

    • 构造函数
      • 通过this.名称设置实例对象不需要共享的属性和方法(this代表新创建的实例对象)
    • 内置了一个prototype属性,指向一个prototype对象
      • 通过Xxx.prototype.名称设置此prototype对象中存放实例对象需要共享的属性和方法
      • 任何一个prototype对象都有一个constructor属性,指向它的构造函数
  • 调用new 构造函数(args)prototype对象生成一个实例对象

    • 实例对象的属性和方法,分成本地的和继承自prototype对象的
    • 所有的实例对象共享同一个prototype对象
    • 每一个实例有一个_prop_属性,指向prototype对象

定义类:

function Person(){}
Person.prototype={
    constructor:Person,
    name:"leon",
    age:22,
    say:function(){
        alert(this.name+" "+this.age);
    }
}
  • function Person中有一个prototype属性,指向Person prototype对象
  • Person prototype对象中有一个constructor属性,指向function Person
  • 所以可以通过new Person()创建对象
  • 注意:使用json方式构建prototype,需手动指定constructor,若不写,则默认指向Object
  • 原型内存模型: Prototype

创建对象,并测试:

var p1=new Person();
p1.say();        // leon 22

var p2=new Person();
p2.name="Ada";

p1.say();    // leon 22
p2.say();    // Ada 22
  • 通过new Person()创建的对象会有一个_prop_属性,指向Person prototype对象
  • 使用对象.xxx访问对象属性或方法时
    • 先在对象内查找调用
    • 若对象内没有,就会通过_prop属性Person prototype对象中查找
    • eg:
      • p1.name 返回leonPerson prototype对象中的值)
      • p2.name 返回Ada(p2对象定义的值)
    • 注意:这个_prop_属性外部是不能访问的
  • 原型内存模型: Prototype

检测对象:

Person.prototype.isPrototypeOf(p1);        //true
p1.hasOwnProperty("name");                //false
//检测某个属性是否在对象中(对象空间或原型空间)
alert("name" in p1);                            //true
alert("address" in p2);                        //false

Person.prototype.isPrototypeOf(p2);        //true
p2.hasOwnProperty("name");                //true

for(var prop in p1) { alert("p1["+prop+"]="+p1[prop]); }
  • isPrototypeOf():判断某个proptotype对象和某个实例之间的关系
  • hasOwnProperty():判断某一个属性是否是本地属性
  • in运算符
    • 判断某个实例是否含有某属性,不管是不是本地属性
    • 还可以用来遍历某个对象的所有属性

注意:

若在Person prototype对象中定义的属性为引用类型,可能会影响到其他对象获取的值,例如:

Person.prototype.firends=["Tom","Jerry"];
p1.friends.push("LiLi");
console.log(p1.friends);    //["Tom","Jerry","LiLi"];
console.log(p2.friends);    //["Tom","Jerry","LiLi"];

建议在construct中定义属性,在prototype中定义方法:

function Person(){
    this.friends=["Tom","Jerry"];
}
Person.prototype={
    constructor:Person,
    ...
}

继承

父类:

function Parent(name){
    this.name=name;
    this.color=["red","blue"];
}
Parent.prototype={
    constructor:Parent,
    ps:function(){
        alert(this.name+" "+this.color);
    }
}

子类:

function Child(name,age){
    //调用父类构造方法,构造子类属性
    Parent.call(this);
    this.age=age;
}

//使用原型链继承,让Child prototype指向Parent对象
Child.prototype=new Parent();
Child.prototype.say=function(){
    alert(this.name+" "+this.age+" "+this.color);
}

创建对象,并测试:

var c1=new Child("leon",22);
var c2=new Child("Ada",23);

c1.color.push("green");
c1.ps();        // leon ["red","blue","green"]
c1.say();      // leon 22 ["red","blue","green"]

c2.ps();        // Ada ["red","blue"]
c2.say();      // Ada 23 ["red","blue"]

内存模型:

Prototype

闭包

定义的函数:

  • 使用function xxx(){...}(会先被初始化):
      fn();
      function fn(){
          ...
      }
    
  • 使用var xxx=function(){...}
      fn2();        // 会报错
      var fn2=function(){
          ...
      }
    

闭包:

通过返回函数来扩大函数的作用域的方式

function func(){
    ...
    return function(){
        ...
    }
}

创建匿名函数并调用(建议这样放入全局变量,控制在一个作用域中):

(function(){
    //...
})()

使用举例:

类中设置私有变量(即没有this.xxx定义,无法通过对象.xxx获取和更改)

var Person;
//调用匿名函数(在匿名函数中进行类和私有变量的定义)
(function(){
    var name="";    //在此匿名函数外部无法获取,且在函数结束时销毁
    Person=function(name){
        name=name;
    };
    Person.prototyep.setName=function(name){
        name=name;
    };
    Person.prototype.getName=function(){
        return name;
    }
})();

测试:

var p1=new Person("Tom");
p1.getName();                //Tom
p1.setName("Lucy");
p1.getName();                //Lucy
//p1.name为undefined,无法获取到