面向对象编程

      //自己手写案例
      function
Fruits(){ // 构造函数 } Fruits.prototype={ // 原型函数 name:"Joe", age:16, init:function(){ console.log('hello world') } } Apple=new Fruits(); //实例对象 Apple.init();//hello world Apple.init=function(){ console.log('我是实例方法'); } Apple.init();//我是实例方法 // console.log(Apple.__proto__) //实例可以通过原型链找到原型 console.log(Apple) console.log(Fruits.prototype) // console.log(Apple.constructor == Fruits) //false(如果没有原型对象,此处会为true,应该每一个实例都有一个constructor属性,默认调用prototype对象的constructor属性
//          console.log(Apple instanceof Fruits) //true  Apple是Fruits原型对象的实例对象

  以上案例,包括了构造函数,原型函数和实例对象,实例对象可以通过__proto__原型链找到原型,实例对象也可以自己定义函数,比如例子中的Apple.init,此时实例对象本身和原型对象上都有init帆方法,此时实例对象会优先调用本身的 方法,如果没有,再去prototype上找。

理解原型对象

  无论什么时候,只要创建一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性是一个指向prototype属性所在函数的指针。

       function New(){
                
            }
            console.log(New.prototype)
       console.log(New.prototype.constructor==New) //true

而通过这个构造函数,我们还可以为原型对象添加其他的属性和方法。

  创建了自定义的构造函数之后,其原型对象默认只会取得constructor属性,至于其他方法,则都是从Object继承来的。当构造函数创建了一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。

ECMA-262第5版中管这个指针交[[Prototype]]。虽然在脚本中没有标准的方式访问[[Prototype]],但Firefox,Safari和Chrome在每个对象上都支持一个属性__proto__;而在其他实现中,这个属性对脚本则是完全不可见的。不过,要

明确的真正重要的一点是,这个连接存在于实例和构造函数的原型对象之间,而不是存在于实例与构造函数之间。(换句话说,实例对象与构造函数没有直接的关系)

方法:

isPrototypeOf()

如果[[Prototype]]指向调用isPrototypeOf()方法的对象(New.prototype),那这个方法就返回true

      function New(){
                
            }
            New.prototype={
                name:'jane',
                getName:function(){
                    console.log(this.name);
                }
            }
            var fn=new New();
            console.log(New.prototype.isPrototypeOf(fn));//true

在ECMAScript5中新增了一个方法,叫Object.getPrototypeOf(),在所有支持的实现中,这个方法返回[[Prototype]]的值,例如:

Object.getPrototypeOf(fn)==New.prototype;//true
Object.getPrototypeOf(fn).name;//jane

Object.getPrototypeOf()返回的对象实际就是这个对象的原型。使用这个方法可以方便的取得一个对象的原型。

每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性。搜索首先是从对戏那个实例本身开始。如果在实例中找到了具有给定名字的属性,则返回该属性的值;如果没有找到,则继续搜索指针指向的原型对象,在原型对象中

查找具有给定名字的属性。如果在原型对象中找到了这个属性,则返回该属性的值。

首先js中对象(函数(function)也是对象),可以访问到原型(prototype),
然后你通过对象得到的原型(prototype)中也会有一个给对象的指针(即constructor)
例如 :
function Person(){};
var pro = Person.prototype;//得到对象的原型 
var obj = pro.constructor ; // 这样可以得到Person对象。
对象创建对象的实例就不多说了 
var p = new Person(); // 创建一个对象实例 。
对象实例中也会隐含的包含一个指向prototype的指针(在safrai ,firefox,chrome,opera等浏览器中可以使用 '__proto__'来访问)
例如:
var p2 = new Person();
var proro = p2.__proto__ ;// 可以得到对象原型(prototype)的引用 ,
var bool = p2.__proto__ == Person.prototype;
alert(bool); // true 对象的原型和实例获取的原型是同一个对象,
总结: 
对象和实例都可以访问到原型 
对象:Person.prototype;
实例:p.__proto__ ;
原型可以访问到对象
Person.prototype.constructor;
则实例也可以访问到对象
p.__proto__.constructor ;

 

读阮一峰文章总结

一 生成对象的原始模式

 假设把一个猫看成一个对象,它有‘颜色’和‘名字’两个属性,

 

var Cat={
  name:''.
  color:''    
}

 

现在我们需要根据原型对象的规格,生成两个实例

var cat1 = {}; // 创建一个空对象
    cat1.name = "大毛"; // 按照原型对象的属性赋值
    cat1.color = "黄色";
var cat2 = {};
    cat2.name = "二毛";
    cat2.color = "黑色";

好了,这就是最简单的封装了,把两个属性封装在一个对象里面。但是,这样的写法有两个缺点,一是如果多生成几个实例,写起来就非常麻烦;而是实例与原型之间,没有任何的办法,可以看出有什么联系。

二  原始模式的改进

我们可以写一个函数,解决代码重复的问题

       function Cat(name,color){
                return{
                    name:name,
                    color:color
                }
            }            

然后生成实例对象,就等于是在调用函数

var cat1=new Cat('大黄','黄色');
console.log(cat1)//{name: "大黄", color: "黄色"}
var cat2=new Cat('二黄','白色');
console.log(cat1)//{name: "二黄", color: "白色"}

这种方法的问题依然是,cat1和cat2之间没有内在的联系,不能反映出它们是同一原型对象的实例。

三 构造函数模式

为了解决从原型对象生成实例的问题,javascript提供了一个构造函数(Constructor)模式

所谓构造函数,其实就是一个普通函数,但是内部使用了this变量。对构造函数使用new 构造符,就能生成实例,并且this变量会绑定在实例对象上。

比如,猫的原型对象 现在可以这样写,

 function Cat(name,color){
    this.name=name;
    this.color=color;
  }

现在就可以生成实例对象了。

  var cat1 = new Cat("大毛","黄色");
  var cat2 = new Cat("二毛","黑色");
  alert(cat1.name); // 大毛
  alert(cat1.color); // 黄色

这时cat1cat2会自动含有一个constructor属性,指向它们的构造函数。

alert(cat1.constructor == Cat); //true
alert(cat2.constructor == Cat); //true

Javascript还提供了一个instanceof运算符,验证原型对象与实例对象之间的关系。

alert(cat1 instanceof Cat); //true
alert(cat2 instanceof Cat); //true

四 构造函数的问题

构造函数方法很好用,但是存在一个浪费内存的问题。

请看,我们现在为Cat对象添加一个不变的属性type(种类),再添加一个方法eat(吃)。那么,原型对象Cat就变成了下面这样:

function Cat(name,color){
    this.name = name;
    this.color = color;
    this.type = "猫科动物";
    this.eat = function(){alert("吃老鼠");};
  }
 //还是采用同样的方法生成实例
var cat1 = new Cat("大毛","黄色");   var cat2 = new Cat ("二毛","黑色");   alert(cat1.type); // 猫科动物   cat1.eat(); // 吃老鼠

 

表面上好像没什么问题,但是实际上这样做,有一个很大的弊端。那就是对于每一个实例对象,type属性和eat()方法都是一模一样的内容,每一次生成一个实例,都必须为重复的内容,多占用一些内存。这样既不环保,也缺乏效率。

  alert(cat1.eat == cat2.eat); //false

能不能让type属性和eat()方法在内存中只生成一次,然后所有实例都指向那个内存地址呢?回答是可以的。

五、 Prototype模式

Javascript规定,每一个构造函数都有一个prototype属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。

这意味着,我们可以把那些不变的属性和方法,直接定义在prototype对象上。

  function Cat(name,color){

    this.name = name;

    this.color = color;

  }

  Cat.prototype.type = "猫科动物";

  Cat.prototype.eat = function(){alert("吃老鼠")};

然后,生成实例。

  var cat1 = new Cat("大毛","黄色");

  var cat2 = new Cat("二毛","黑色");

  alert(cat1.type); // 猫科动物

  cat1.eat(); // 吃老鼠

这时所有实例的type属性和eat()方法,其实都是同一个内存地址,指向prototype对象,因此就提高了运行效率。

  alert(cat1.eat == cat2.eat); //true

六、 Prototype模式的验证方法

为了配合prototype属性,Javascript定义了一些辅助方法,帮助我们使用它。,

6.1 isPrototypeOf()

这个方法用来判断,某个proptotype对象和某个实例之间的关系。

  alert(Cat.prototype.isPrototypeOf(cat1)); //true

  alert(Cat.prototype.isPrototypeOf(cat2)); //true

6.2 hasOwnProperty()

每个实例对象都有一个hasOwnProperty()方法,用来判断某一个属性到底是本地属性,还是继承自prototype对象的属性。

  alert(cat1.hasOwnProperty("name")); // true

  alert(cat1.hasOwnProperty("type")); // false

6.3 in运算符

in运算符可以用来判断,某个实例是否含有某个属性,不管是不是本地属性。

  alert("name" in cat1); // true

  alert("type" in cat1); // true

in运算符还可以用来遍历某个对象的所有属性。

  for(var prop in cat1) { alert("cat1["+prop+"]="+cat1[prop]); }

 

 七  原型链

我们知道所有的函数都有一个叫做toString的方法。那么这个方法到底是在哪里的呢?

先随意声明一个函数:

function add() {}

那么我们可以用如下的图来表示这个函数的原型链。

原型链

其中add是Function对象的实例。而Function的原型对象同时又是Object原型的实例。这样就构成了一条原型链。原型链的访问,其实跟作用域链有很大的相似之处,

他们都是一次单向的查找过程。因此实例对象能够通过原型链,访问到处于原型链上对象的所有属性与方法。这也是foo最终能够访问到处于Object原型对象上的toString方法的原因。

基于原型链的特性,我们可以很轻松的实现继承。

参考:

http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_encapsulation.html

http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance.html

http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance_continued.html

posted @ 2017-12-07 16:44  伊优  阅读(202)  评论(0编辑  收藏  举报