谈谈我认识的js原型

  众所周知,JavaScript中是没有传统类的概念的,js通过原型链的方式实现继承。原型是js学习中的一大重点知识,在ES6出来之前,因为js不像php、java一样拥有类的写法,所以继承方式也就不像php、java一样通过关键字extends实现子类继承父类的方式,但是js有它独特的实现继承的方式,这种方式有:prototype、原型冒充、复制继承。

1、prototype

  js中每个函数就是一个对象,每个对象都有自己的prototype属性,通过这个属性,可以指定某一类对象的原型

 function   Person(name,age,sex){
    this.name = name || " ", //设置默认值,如果传入实参,this.name的值就等于实参的值,否则this.name的值默认为 " "
    this.age    = age || 24,
    this.sex    = "男" || sex,
    this.say = function (){
      console.log("我会说话");
    },
    this.smile = function(){
      console.log("笑");
    },
    this.walk = function(){
      console.log("走路");
    }
  };

function  Monkey(){
    this.type = "monkey";
};
var  oop = new Person();
Monkey.prototype = oop;
var  oom = new Monkey();
//执行oom.say()输出 我会说话
oom.say();

oom对象并没有say()方法,为什么程序没有保存而且控制台还输出了 “我会说话”。因为oom对象的构造函数的prototype属性指向oop对象,表明由Monkey构造函数创建出来的一类对象,他们都具有共同的原型:oop对象。所以oom对象调用say()方法中,程序会默认先在oom对象中查找,如果没有找到这个属性,程序会沿着原型链往上寻找该属性。

需要注意的是,js中new  构造函数()这个过程发生了如下的操作:

a、创建空对象 { };

b、this指向空对象{ };

c、执行构造函数的函数体。

在过程c中,如果你熟悉js的为对象添加属性的方式(对象名 属性),你会发现,过程c其实就是给这个空对象添加属性和方法的过程。

构造函数名 。prototype = 对象 ,这个对象也可以是自定义的对象,在这个对象中定义了需要用到的方法和值。如下例子所示:

function   op(){
  a = 3;
};

var  obj = {
  para : 1,
  say : function(){console.log("obj");}
};
//指定构造函数op的原型是obj对象;
op.prototype = obj;

//由构造函数op创建的oop对象能够调用对象obj中的方法
var oop = new op();
oop.say();

2、原型冒充

  原型冒充的原理是通过call()与apply()函数改变当前对象的this的执行上下文。这话说的有点抽象,下面直接用例子解释:

 

 //函数
  function   increase(a , b){
    return   a+b;
  };

  function    reduce(a , b){
    return  a-b;
  }
  increase . call(reduce,1,2);  //输出3

  reduce . call(increase,1,2);  //输出-1
 
解释 :对象A . call(对象B , 参数1 , 参数2  , ......参数n)中执行了这样的操作:

a、修改对象A的this的指向,使其暂时指向对象B,参数1与参数2是传入对象B的实参。

b、执行调用call()方法的对象。

所以increase . call(reduce,1,2); 这一句相当于increase中this指向reduce,然后执行increase(1,2),所以最终结果输出3。

 //对象

function person(name){
    this.name = name;
    this.say = function(){
    console.log(this.name);
    }
};
    var op = new person("张三");
    op.say();

function lisi(name){
    this.name = name;
};
    var ls = new lisi("李四");
    op.say.call(ls);//this指向ls :op.say(){ console.log(ls.name) },执行op.say()方法,输出 李四

op.say.call(ls)这句代码先是将say()函数体内的this指向ls,然后执行say()方法。op.say();这一步在正常情况下输出的结果应该是"张三",但是为什么却是“李四”了呢?原因是op.say.call(ls)这句代码将say()函数在程序解析到这句代码的位置时,this暂时指向对象ls,所以在op对象的say()体内的输出语句console.log(this.name)实际上是console.log(ls.name);

3、复制继承

  复制继承的原理是,将对象以参数的形式传递到构造函数中,在构造函数中通过对象属性的方式(Objname . propertyname或者Objname[propername])访问盖对象的属性值,将值赋予当前对象的属性。

  

function   Person(){
    this.name = "张三";
    this.age  = 24;
    this.sex  = "男"
}

function  children(per){
    this.name = per.name;
    this.age  = per.age;
    this.sex  = per.sex;

}
var   op = new  Person();
//向children构造函数中传入op对象,在children函数内完成值的复制
var   chi = new  children(op);
console.log(chi.age);

 以上均是js原型继承的实现方式,在js这个海洋中,因为知识有限,所以博客还存在很多不足之处,希望更够跟大家多多交流学习。

原文地址:https://www.cnblogs.com/novice007/p/7764583.html