谈谈我认识的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这个海洋中,因为知识有限,所以博客还存在很多不足之处,希望更够跟大家多多交流学习。