浅谈JS继承

今天呢,我们来谈谈继承,它也是JS语言中的一大重点,一般什么时候我们会用继承呢,比如有两个拖拽的面板,两个功能基本一致,只是第二个面板多了一些不同的东西,这个时候,我们就会希望,要是第二个直接能继承第一个面板相同的功能就好了。所以这个时候继承就登场啦。。。

  继承:在原有对象的基础上,略作修改,得到一个新的对象,并且不影响原有对象的功能

在具体讲继承前,首先来了解一个东西,这个东西叫做原型链,是继承的基础。我们先来看一段代码:

function Aaa(){}
Aaa.prototype.num = 3;
var a1 = new Aaa();
alert(a1.num); //3   a1是怎么找到的原型下面的num呢?

这是一个很简单的代码,但是我们有没有想过一个问题,既然我们把变量放到了原型下面,那么a1是怎么找到这个变量的呢?实际上就是通过原型链啦。

原型链是指:实例对象与原型之间的连接,__proto__(它是一种隐式连接 ,我们看不到,但确实存在,a1能跟着这个链找到对应的原型下面的东西)。

看下面的这个图:首先a1先看看自己的下面有没有num,发现没有之后,就会随着原型链,找找找,找到了原型,发现下面有,就弹出了这个num.打开firebug可以看到num是通过原型链中原型下被找到的。

那如果是下面这种情况呢:

function Aaa(){
   this.num =10;
}
Aaa.prototype.num = 3;
var a1 = new Aaa();
alert(a1.num); //10

我们之前讲过原型的优先级是比较低的,其实也是原型链的原因,对象会首先在自己的地盘找,找不到才会从原型链上找,而且最外层的原型链是object,所以上面的意思其实下面就是这样的:

   1 拷贝继承

首先来看个栗子:父类有属性name,age, 方法show,子类有属性name,age,sex,怎么能让子类直接继承父类的属性和方法呢?

对于属性的继承:其实我们可以在子类里面直接调用父类的函数。

方法的继承:因为方法在原型下面,原型本身也是一个对象,我们可以直接把父类的原型对象赋给子类的原型对象。那么子类就会有父类的方法了。

function Create(name,age){
    this.name = name;
    this.age = age;
}
Create.prototype.show = function(){
    alert(this.name);
}
function Create2(name,age,sex){
    Create(name,age); //这样调用this指的是window
    this.sex = sex;
}
Create2.prototype = Create.prototype;  //这样对象赋值会存在引用关系

但是这个方法存在两个问题:

1  因为在子类中,我们希望的是this指的是当前的对象,但直接这样调用就指的是window,所以我们需要用call()方法修改一下this的指向。

2  对象复制会存在引用问题,也就是说现在两个对象指向的是同一个地址,其中一个原型的一些东西的更改会影响另一个,这肯定不是我们所希望的。所以我们通过拷贝赋值,来避免引用。

function Create(name,age){
    this.name = name;
    this.age = age;
}
Create.prototype.show = function(){
    alert(this.name);
}
function Create2(name,age,sex){
    Create.call(this,name,age);
    this.sex = sex;
}
extend(Create2.prototype ,Create.prototype ); //函数之间赋值不会存在引用
Create2.prototype.showJob = function(){}; //不会影响父类
var p2 = new Create2(‘hua‘,11,‘men‘);
p2.show();   // hua
function extend(obj1,obj2){
      for(var attr in obj2){   //循环遍历对象下面的属性和方法,进行赋值,需要知道的是函数之间的复制是不存在引用的,所以方法之间就不存在引用了
           obj1[attr] = obj2[attr];
     }
}

这种继承方式叫做拷贝继承。(jquery也是采用拷贝继承extend)

2 类式继承(利用构造函数继承,一句话即可完成继承)

来看下面的代码:

function Aaa(){   //父类
    this.name = [1,2,3];
}
Aaa.prototype.showName = function(){
    alert( this.name );
};
function Bbb(){ }//子类
Bbb.prototype = new Aaa();
//这句话覆盖了Bbb所有的原型,所以构造函数指向改变(这里不懂的话,参建之前的博客,JS面向对象之创建对象中讲到的constructor)
//这句话就可以让子类找到父类的方法和属性
var b1 = new Bbb();b1.showName();//弹出[1,2,3]
alert(b1.constructor);//Aaa 可见构造函数指向变了
b1.name.push(4);// 改变b1.name
var b2 = new Bbb();
alert(b2.name) // [1,2,3,4]  由此可见属性存在引用

我们通过看一个图看理解是怎么继承的:

Bbb.prototype = new Aaa(); 这句话,使得图上的a1和Bbb的原型指向了同一个地方,所以当指向b1.showName();这句的时候,它现在自己下寻找有没有这个方法,没有就通过原型链去原型下找,发现也没有,又通过原型链去父级那边找,最终就找到了。

不过通过注释可以看到,其实这种方法其实是存在一些问题的:

1 构造函数指向问题      2 属性存在引用

解决:1 修正构造函数指向  2 方法和属性(call)分开继承
代码如下:1 属性继承和拷贝继承一样,通过构造函数调用继承  2 方法继承,通过中间人来做到之继承方法(看注释应该能懂,也可以自己试着画原型链图理解)
function Aaa(){ //父类
this.name = [1,2,3];
}
Aaa.prototype.showName = function(){
alert( this.name );
};
function Bbb(){   //子类
    Aaa.call(this);    //属性继承
}
var F = function(){}; //声明空的构造函数,中间媒介
F.prototype = Aaa.prototype;//把Aaa的原型赋给F,这个时候F会拥有Aaa原型下所有的方法
Bbb.prototype = new F();  //这个同上,Bbb会用于F的所有方法和属性,因为上一句复制F只是拥有了Aaa的方法,所以这里实质上Bbb拥有的也只是Aaa的方法
Bbb.prototype.constructor = Bbb; //修正指向问题
var b1 = new Bbb();
b1.name // [1,2,3]
b1.constructor;//Bbb 指向正常
b1.name.push(4);
var b2 = new Bbb();
b2.name // [1,2,3]   属性互相不影响

3 原型继承(借助原型来实现对象继承对象)

var a = {
    name : ‘小明‘
};

var b = cloneObj(a); 

b.name = ‘小强‘;
alert( b.name );//小强 会优先在自己的下面找name
alert( a.name );//小明
function cloneObj(obj){
      var F = function(){}; //声明一个构造函数
      F.prototype = obj; //F原型下拥有obj的方法和属性
      return new F(); // 返回声明通过F声明的对象,通过上一句知道,这个对象具有obj的方法和属性
 }

以上就是三种继承方法,其实也有别的,后续可以继续补充,对于继承也许用的不是很多,但对理解JS语言还是很重要的。一起加油把。

				
时间: 2024-10-13 22:39:45

浅谈JS继承的相关文章

浅谈js中的this关键字

浅谈js中的this关键字 全局作用域中的this 函数作用域中的this 不同函数调用方法下的this 直接调用 作为对象的方法调用 作为构造函数调用 通过call或apply方法调用 嵌套函数作用域中的this 浅谈js中的this关键字 this是JavaScript中的关键字之一,在编写程序的时候经常会用到,正确的理解和使用关键字this尤为重要.接下来,笔者就从作用域的角度粗谈下自己对this关键字的理解,希望能给到大家一些启示,权当交流之用. 全局作用域中的this 本文将以作用域由

浅谈 js eval作用域

就简单聊下如何全局 eval 一个代码. var x = 1; (function () { eval('var x = 123;'); })(); console.log(x); 这个代码得到的是 1 而不是 123如果想让 eval 执行的代码是全局的,那么有几种方法. var x = 1; (function () { window.eval('var x = 123;'); })(); console.log(x); 这个方法标准浏览器都可以得到 123 而IE6-8则依然是 1 相同的

浅谈 js 正则之 test 方法

原文:浅谈 js 正则之 test 方法 其实我很少用这个,所以之前一直没注意这个问题,自从落叶那厮写了个变态的测试我才去看了下这东西.先来看个东西吧. ? 1 2 3 4 5 var re = /\d/; console.log( re.test("1") ); console.log( re.test("1") ); console.log( re.test("1") ); console.log( re.test("1"

浅谈 js 语句块与标签

原文:浅谈 js 语句块与标签 语句块是什么?其实就是用 {} 包裹的一些js代码而已,当然语句块不能独立作用域.可以详细参见这里<MDN block> 也许很多人第一印象 {} 不是对象字面量么?怎么成了语句块了?如果在赋值语句或者表达式里用的时候,确实是对象字面量,如: var a = {}; ({toString:function(){return "hehe"}}) + "..."; 是不是很有意思..但是直接使用如: {toString: fu

浅谈JS之AJAX

0x00:什么是Ajax? Ajax是Asynchronous Javascript And Xml 的缩写(异步javascript及xml),Ajax是使用javascript在浏览器后台操作HTTP和web服务器进行数据交换(用户不知道也感觉不出来,就跟桌面应用程序似的进行数据交互),它不会导致页面重新加载,这样才有更好的用户体验. Ajax是基于以下开放标准: javascript(DOM) css html xml(json) 通俗的说就是使用了javascript(DOM)的XMLH

浅谈 js 下 with 对性能的影响

这几天多次看到有博主们在写 with 的文章,这货确实非常方便,但是却是个性能杀手,所以一直都是上不得台面的.那么他究竟会让效率低下到什么程度呢?先来看下 with 是如何的便捷吧.. // 正常调用 console.log(location.host); console.log(location.pathname); // 在 with 下 with (location) { console.log(host); console.log(pathname); } 如果不影响性能,确实是非常霸气

浅谈 js 数字格式类型

原文:浅谈 js 数字格式类型 很多人也许只知道 123,123.456,0xff 之类的数字格式.其实 js 格式还有很多数字格式类型,比如 1., .1 这样的,也有 .1e2 这样的. 可能有人说这是什么个格式?其实还不止呢.1          //11.2       //1.21.2e3    //12001.2e+3  //12001.2e-3  //0.0012.12e+2 //12-.12e-2 //-0.0012 当然这些只是十进制.我们来说说 八进制 和 十六进制.0x00

浅谈 js 字符串 trim 方法之正则篇

position:static(静态定位) 当position属性定义为static时,可以将元素定义为静态位置,所谓静态位置就是各个元素在HTML文档流中应有的位置 podisition定位问题.所以当没有定义position属性时,并不说明该元素没有自己的位置,它会遵循默认显示为静态位置,在静态定位状态下无法通过坐标值(top,left,right,bottom)来改变它的位置. position:absolute(绝对定位) 当position属性定义为absolute时,元素会脱离文档流

从window.console&amp;&amp;console.log(123)浅谈JS的且运算逻辑(&amp;&amp;)

从window.console&&console.log(123)浅谈JS的且运算逻辑(&&) 作者:www.cnblogs.com  来源:www.cnblogs.com  发布日期:2015-03-01 一.JS的且运算记得最开始看到window.console&&console.log(123),当时知道能起什么作用但是没有深入研究,最近在研究后总算弄明白了.要理解这个,首先得明白三个知识点第一:短路原则这个大家都非常清楚的了,在做且运算的时候,“同真