乐趣区

关于javascript:JavaScript-进阶教程全天在线实时计划快三10319281

目录
加一 Q 一带一你10319281邀一情一玛33339333进【c9183.com】已助上千人成功翻盘,欢迎增加,沟通交流!
1 原型链

1.1 原型

1.1.1 什么是原型?

1.1.2 原型的作用: 数据共享, 节俭内存空间

1.1.3 原型的写法:

1.1.4 通过原型为内置对象增加原型的属性或者办法

1.2 原型链

1.3 原型的指向

1.3.1 原型的指向是能够扭转的

1.3.2 原型的最终指向

1.3.3 在原型扭转指向之后增加原型办法

1.4 实例对象属性和原型对象属性重名

1.5 一条神奇的原型链

2 继承

2.1 什么是继承

2.2 通过原型实现继承

2.3 继承实例

2.4 借用构造函数实现继承

2.5 组合继承

2.6 另一种继承形式:拷贝继承(for-in)

3 总结


1 原型链

1.1 原型

对于原型在 JS 面向对象编程这篇文章曾经讲过了,明天简略来温习一下。

1.1.1 什么是原型?

在 JS 构造函数中有一个属性 prototype, 叫做原型, 这是给程序员应用的。在 JS 实例对象中有一个属性__proto__, 它也是原型, 这是供浏览器应用的, 它不是规范的属性。 实例对象中的__proto__指向的是该实例对象中的构造函数中的 prototype,构造函数中的 prototype 外面的属性或者办法, 能够间接通过实例对象调用。

个别状况下,实例对象.__proto__能力拜访到构造函数中的 prototype 的属性或者办法,即 per._proto_.eat()。因为__proto__不是规范的属性,所以间接写成 per.eat()即可,原型是一个属性, 而这个属性也是一个对象。

1.1.2 原型的作用: 数据共享, 节俭内存空间

在构造函数中定义的属性和办法, 当实例化对象的时候, 实例对象中的属性和办法都是在本人的空间中存在的, 如果是多个对象。这些属性和办法都会在独自的空间中存在, 节约内存空间, 所以, 为了数据共享, 把想要节俭空间的属性或者办法写在原型对象中, 达到了数据共享,节俭了内存空间。

1.1.3 原型的写法:

构造函数.prototype. 属性 = 值
构造函数.prototype. 办法 = 值 —-> 函数.prototype, 函数也是对象, 所以, 外面也有__proto__
实例对象.prototype——–> 实例对象中没有这个属性, 只有__proto__
原型的简略写法
缺点:—> 原型间接指向{}—-> 就是一个对象, 没有结构器

 1.   构造函数.prototype={
    
2.   切记: 如果这这种写法, 要把结构器加上
    

4.   };

1.1.4 通过原型为内置对象增加原型的属性或者办法

零碎的内置对象的属性和办法可能不满足当初需要,所以能够通过原型的形式退出属性或者办法。为内置对象的原型增加属性和办法,这个内置对象的实例对象能够间接应用增加的属性或办法。



1.  // 为内置对象增加原型办法
    
2.  // 咱们在零碎的对象的原型中增加办法, 相当于在扭转源码
    
3.  // 我心愿字符串中有一个倒序字符串的办法
    
4.  String.prototype.myReverse = function() {5.      for (var i = this.length - 1; i >= 0; i--) {6.          console.log(this[i]);
    
7.      }
    
8.  };
    
9.  var str = "abcdefg";
    
10.  str.myReverse();
    

13.  // 为 Array 内置对象的原型对象中增加办法
    
14.  Array.prototype.mySort = function() {15.      for (var i = 0; i < this.length - 1; i++) {16.          for (var j = 0; j < this.length - 1 - i; j++) {17.              if (this[j] < this[j + 1]) {18.                  var temp = this[j];
    
19.                  this[j] = this[j + 1];
    
20.                  this[j + 1] = temp;
    
21.              } //end if
    
22.          } // end for
    
23.      } //end for
    
24.  };
    

26.  var arr = [100, 3, 56, 78, 23, 10];
    
27.  arr.mySort();
    
28.  console.log(arr);
    

31.  String.prototype.sayHi = function() {32.      console.log(this + "哈哈, 我又变帅了");
    
33.  };
    

35.  // 字符串就有了打招呼的办法
    
36.  var str2 = "小杨";
    
37.  str2.sayHi();
    

1.2 原型链

原型链是一种关系, 是实例对象和原型对象之间的关系, 这种关系是通过原型 (proto) 来分割的。JavaScript 对象有一个指向一个原型对象的链。当试图拜访一个对象的属性时,它不仅仅在该对象上搜查,还会搜查该对象的原型,以及该对象的原型的原型,顺次层层向上搜寻,直到找到一个名字匹配的属性或达到原型链的开端。
请看下边的代码:

 1.     // 人的构造函数
    
2.      function Person(name,age) {
    
3.        // 属性
    
4.        this.name=name;
    
5.        this.age=age;
    
6.        // 在构造函数中的办法
    
7.        this.eat=function () {8.          console.log("吃吃吃");
    
9.        };
    
10.      }
    
11.      // 增加共享的属性
    
12.      Person.prototype.sex="男";
    
13.      // 增加共享的办法
    
14.      Person.prototype.sayHi=function () {15.        console.log("哈哈哈");
    
16.      };
    
17.      // 实例化对象, 并初始化
    
18.      var per=new Person("张三",18);
    
19.      per.sayHi();

上边的代码中实例对象的原型__proto__和构造函数的原型 prototype 指向是雷同的,实例对象中的__proto__原型指向的是构造函数中的原型 prototype。

console.log(per.__proto__==Person.prototype);// true

它们的关系如下:

上图中红线链接局部即 Person 实例对象和 Person 原型对象之间的关系,能够看成一个原型链(不残缺,目前能够先这样了解)。

1.3 原型的指向

1.3.1 原型的指向是能够扭转的

请看上面的代码:

 1.     // 人的构造函数
    
2.      function Person(age) {
    
3.        this.age=10;
    
4.      }
    
5.      // 人的原型对象办法
    
6.      Person.prototype.eat=function () {7.        console.log("吃吃吃!!!");
    
8.      };
    
9.      // 学生的构造函数
    
10.      function Student() {12.}
    
13.      Student.prototype.sayHi=function () {14.        console.log("学学学,为中华崛起而读书!!!");
    
15.      };
    
16.      // 学生的原型, 指向了一个人的实例对象
    
17.      Student.prototype=new Person(18);
    
18.      var stu=new Student();
    
19.      console.dir(stu);
    
20.      stu.eat(); // 吃吃吃!!!
    
21.      stu.sayHi();// 谬误 stu.sayHi is not a function,原型指向产生扭转,找不到 sayHi()办法

输入后果:

上边的代码中,实例对象 stu 调用原型办法 sayHi()的时候产生谬误,这是因为 Student.prototype=new Person(18); 使原型指向产生了扭转,stu 对象中曾经找不到 sayHi()办法。原型指向扭转剖析:图中的红线形成原型链。

1.3.2 原型的最终指向

通过上边的学习咱们能够晓得,实例对象中有__proto__原型,构造函数中有 prototype 原型,prototype 也是是一个对象,那么,prototype 这个对象中应该也有__proto__,那么它又指向了哪里呢?
即然实例对象中的__proto__指向的是构造函数的 prototype,所以 prototype 这个对象中__proto__指向的应该是某个构造函数的原型 prototype。

 1.      function Person() {3.}
    
4.      Person.prototype.eat=function () {5.        console.log("吃吃吃");
    
6.      };
    

8.      var per=new Person();
    
9.      console.dir(per);
    
10.      console.dir(Person);


通过剖析咱们发现 Person 的 prototype 中的__proto__的指向为 Object;Object.prototype 的__proto__是 null。

console.log(Object.prototype.__proto__);// null

所以 per 实例对象的原型的指向为:

per 实例对象的__proto__——->Person.prototype,Person.prototype 的__proto__—->Object.prototype,Object.prototype 的__proto__是 null。这形成了一个残缺的原型链。

上图中的红线链接形成了一个残缺的原型链。

1.3.3 在原型扭转指向之后增加原型办法

在 1.3.1 的代码中,因为原型指向产生扭转,找不到 sayHi()办法而产生谬误,咱们能够在原型扭转指向之后增加原型办法, 扭转代码如下:

 1.      // 人的构造函数
    
2.      function Person(age) {
    
3.        this.age=10;
    
4.      }
    
5.      // 人的原型对象办法
    
6.      Person.prototype.eat=function () {7.        console.log("吃吃吃!!!");
    
8.      };
    
9.      // 学生的构造函数
    
10.      function Student() {12.}
    
13.      // 学生的原型, 指向了一个人的实例对象
    
14.      Student.prototype=new Person(18);
    
15.      Student.prototype.sayHi=function () {16.            console.log("学学学,为中华崛起而读书!!!");
    
17.      };
    
18.      var stu=new Student();
    
19.      console.dir(stu);
    
20.      stu.eat(); // 吃吃吃!!!
    
21.      stu.sayHi();// 学学学,为中华崛起而读书!!!


留神:简略的原型写法,会将 Person.prototype 重置到了一个新的对象,即扭转原型的指向。咱们也应该在原型扭转指向之后增加原型办法。

 1.     function Person(age) {
    
2.        this.age = age;
    
3.      }
    

5.      // 指向扭转了
    
6.      Person.prototype = {7.        eat: function () {8.          console.log("吃");
    
9.        }
    
10.      };
    
11.      // 增加原型办法
    
12.      Person.prototype.sayHi = function () {13.        console.log("你好帅呀!!!");
    
14.      };
    
15.      var per = new Person(10);
    
16.      per.sayHi();// 你好帅呀!!!

1.4 实例对象属性和原型对象属性重名

实例对象拜访某个属性, 应该先从实例对象中找, 找到了就间接用,找不到就去指向的原型对象中找, 找到了就应用, 找不到返回 undefined。

 1.      function Person(age,sex) {
    
2.        this.age=age;
    
3.        this.sex=sex;
    
4.      }
    
5.      Person.prototype.sex="女";
    
6.      var per=new Person(18,"男");
    
7.      console.log(per.sex); // 男
    
8.      // 因为 JS 是一门动静类型的语言, 如果对象没有这个属性, 只有对象. 属性名字, 对象就有了这个属性了
    
9.      // 然而该属性没有赋值, 所以 per.albert 后果是:undefined
    
10.      console.log(per.albert); // undefined
    
11.      console.log(albert); // 报错

通过实例对象不能扭转原型对象中的属性值,要想扭转原型对象中的属性值, 应该间接通过原型对象. 属性 = 值;进行扭转。

 1.     Person.prototype.sex="我的天呐!!! 我为什么这么帅?";
    
2.     per.sex="不晓得";
    
3.     console.log(per.sex);
    

5.     console.dir(per);

1.5 一条神奇的原型链

1.  <!DOCTYPE html>
    
2.  <html lang="en">
    
3.          <head>
    
4.                  <meta charset="UTF-8">
    
5.                  <title> 微信公众号:AlbertYang</title>
    
6.          </head>
    
7.          <body>
    
8.                  <div id="dv"></div>
    
9.                  <script>
    
10.                          var divObj = document.getElementById("dv");
    
11.                          console.dir(divObj);
    
12.                  </script>
    
13.          </body>
    
14.  </html> 

通过在控制台剖析 divObj,得出 divObj 原型链指向为:

divObj.proto—->HTMLDivElement.prototype 的__proto__—>HTMLElement.prototype 的__proto__—->Element.prototype 的__proto__—->Node.prototype 的__proto__—->EventTarget.prototype 的__proto__—->Object.prototype 没有__proto__, 所以,Object.prototype 中的__proto__是 null

2 继承

面向对象编程思维是依据需要剖析对象,找到对象有什么特色和行为,而后通过代码的形式来实现需求。要想实现这个需要,就要创建对象,要想创建对象,就应该有构造函数,而后通过构造函数来创建对象,通过对象调用属性和办法来实现相应的性能及需要。
因为面向对象的思维适宜于人的想法,编程起来会更加的不便,前期保护的时候也要会更加容易,所以咱们才要学习面向对象编程。但 JS 不是一门面向对象的语言,而是一门基于对象的语言。JS 不像 JAVA,C# 等面向对象的编程语言中有类 (class) 的概念 (也是一种非凡的数据类型),JS 中没有类(class), 然而 JS 能够模仿面向对象的思维编程, 在 JS 能够通过构造函数来模仿类的概念(class)。在 ES6 中,class (类) 作为对象的模板被引入,能够通过 class 关键字定义类,然而它的的实质还是 function。

2.1 什么是继承

继承是一品种 (class) 与类之间的关系,JS 中没有类, 然而能够通过构造函数模仿类, 而后通过原型来实现继承,继承是为了实现数据共享,js 中的继承当然也是为了实现数据共享。

继承是子类继承父类的特色和行为,使得子类对象(实例)具备父类的属性和办法,或子类从父类继承办法,使得子类具备父类雷同的行为。继承能够使得子类具备父类的各种属性和办法,而不须要再次编写雷同的代码。

例如:人有 姓名, 性别, 年龄 , 吃饭, 睡觉等属性和行为。
学生有: 姓名, 性别, 年龄 , 吃饭, 睡觉 学习等属性和行为。
老师有: 姓名, 性别, 年龄 , 吃饭, 睡觉 , 教学等属性和行为。
先定义一个人类,人有姓名, 性别, 年龄等属性,有, 吃饭, 睡觉等行为。由人这个类派生出学生和老师两个类,为学生增加学习行为,为老师增加教学行为。

2.2 通过原型实现继承



1.  // 人
    
2.  function Person(name, age, sex) {
    
3.      this.name = name;
    
4.      this.sex = sex;
    
5.      this.age = age;
    
6.  }
    
7.  Person.prototype.eat = function() {8.      console.log("吃吃吃!!!");
    
9.  };
    
10.  Person.prototype.sleep = function() {11.      console.log("睡睡睡!!!");
    
12.  };
    

15.  // 学生
    
16.  function Student(score) {
    
17.      this.score = score;
    
18.  }
    
19.  // 扭转学生的原型的指向即可让学生继承人
    
20.  Student.prototype = new Person("张三", 18, "男");
    
21.  Student.prototype.study = function() {22.      console.log("学习真的太累了!!!");
    
23.  };
    

25.  var stu = new Student(99);
    
26.  console.log("学生从人中继承的属性和行为:"); // > 学生从人中继承的属性和行为:
    
27.  console.log(stu.name); // > 张三
    
28.  console.log(stu.age); // >18
    
29.  console.log(stu.sex); // > 男
    
30.  stu.eat(); // > 吃吃吃!!!
    
31.  stu.sleep(); // > 睡睡睡!!!
    
32.  console.log("学生中本人有的属性和行为:"); // > 学生中本人有的属性和行为:
    
33.  console.log(stu.score); // >99
    
34.  stu.study(); // > 学习真的太累了!!!

2.3 继承实例

动物有名字, 体重等属性, 还有吃货色的行为。
狗有名字, 体重, 毛色等属性, 还有吃货色和咬人的行为。
哈士奇有名字, 体重, 毛色, 年龄等属性, 还有吃货色,咬人, 逗人玩等行为。
它们之间的继承关系代码实现如下:



1.  // 动物的构造函数
    
2.  function Animal(name, weight) {
    
3.      this.name = name;
    
4.      this.weight = weight;
    
5.  }
    
6.  // 动物的原型的办法
    
7.  Animal.prototype.eat = function() {8.      console.log("弟兄们冲啊,赶快吃吃吃!!!");
    
9.  };
    

11.  // 狗的构造函数
    
12.  function Dog(color) {
    
13.      this.color = color;
    
14.  }
    
15.  Dog.prototype = new Animal("小三", "30kg");
    
16.  Dog.prototype.bitePerson = function() {17.      console.log("~ 汪汪汪~, 快让开, 我要咬人了!!!");
    
18.  };
    

20.  // 哈士奇构造函数
    
21.  function Husky(age) {
    
22.      this.age = age;
    
23.  }
    
24.  Husky.prototype = new Dog("黑红色");
    
25.  Husky.prototype.playYou = function() {26.      console.log("咬坏充电器, 咬坏耳机, 拆家... 哈哈,好玩不!!!");
    
27.  };
    
28.  var husky = new Husky(3);
    
29.  console.log(husky.name, husky.weight, husky.color);
    
30.  husky.eat();
    
31.  husky.bitePerson();
    
32.  husky.playYou();
    

它们的原型链关系为:

2.4 借用构造函数实现继承

在上边的解说中,咱们为了数据共享, 扭转了原型指向, 做到了继承,即通过扭转原型指向实现了继承。这导致了一个问题,因为咱们扭转原型指向的同时, 间接初始化了属性,这样继承过去的属性的值都是一样的了。这是个问题,如果咱们想要扭转继承过去的值,只能从新调用对象的属性进行从新赋值,这又导致咱们上边的初始化失去了意义。



1.  function Person(name, age, sex, weight) {
    
2.      this.name = name;
    
3.      this.age = age;
    
4.      this.sex = sex;
    
5.      this.weight = weight;
    
6.  }
    
7.  Person.prototype.sayHi = function() {8.      console.log("你好帅呀!!!");
    
9.  };
    

11.  function Student(score) {
    
12.      this.score = score;
    
13.  }
    
14.  // 心愿人的类别中的数据能够共享给学生 --- 继承
    
15.  Student.prototype = new Person("小三", 18, "男", "58kg");
    

17.  var stu1 = new Student("99");
    
18.  console.log(stu1.name, stu1.age, stu1.sex, stu1.weight, stu1.score);
    
19.  stu1.sayHi();
    

21.  var stu2 = new Student("89");
    
22.  console.log(stu2.name, stu2.age, stu2.sex, stu2.weight, stu2.score);
    
23.  stu2.sayHi();
    

25.  var stu3 = new Student("66");
    
26.  console.log(stu3.name, stu3.age, stu3.sex, stu3.weight, stu3.score);
    
27.  stu3.sayHi();
    

从新调用对象的属性进行从新赋值,十分的麻烦,而且使咱们上边的 new Person(“ 小三 ”, 18, “ 男 ”, “58kg”); 初始化失去了意义。



1.  var stu3 = new Student("66");
    
2.  stu3.name = "小红";
    
3.  stu3.age = 16;
    
4.  stu3.sex = "女";
    
5.  stu3.weight = "45kg";
    
6.  console.log(stu3.name, stu3.age, stu3.sex, stu3.weight, stu3.score);
    
7.  stu3.sayHi();
    

如何解决上边的问题呢?答案是借用构造函数实现继承。
继承的时候, 不扭转原型的指向, 间接调用父级的构造函数来为属性赋值,即把要继承的父级的构造函数拿过去, 借用一下为属性赋值,这叫做借用构造函数。借用构造函数须要应用 call ()这个办法,我会在后边的文章中进行解说,大家在这里先记住用法就行了。



1.  function Person(name, age, sex, weight) {
    
2.      this.name = name;
    
3.      this.age = age;
    
4.      this.sex = sex;
    
5.      this.weight = weight;
    
6.  }
    
7.  Person.prototype.sayHi = function() {8.      console.log("你好帅呀!!!");
    
9.  };
    

11.  function Student(name, age, sex, weight, score) {
    
12.      // 借用构造函数
    
13.      Person.call(this, name, age, sex, weight);
    
14.      this.score = score;
    
15.  }
    
16.  var stu1 = new Student("小三", 16, "男", "50kg", "110");
    
17.  console.log(stu1.name, stu1.age, stu1.sex, stu1.weight, stu1.score);
    

19.  var stu2 = new Student("小红", 22, "女", "45kg", "88");
    
20.  console.log(stu2.name, stu2.age, stu2.sex, stu2.weight, stu2.score);
    

22.  var stu3 = new Student("小舞", 16, "女", "40kg", "100");
    
23.  console.log(stu3.name, stu3.age, stu3.sex, stu3.weight, stu3.score);
    


借用构造函数继承, 解决了继承的时候属性反复的问题。然而这又导致一个问题即父类中的原型办法不能被继承。



1.  function Person(name, age, sex, weight) {
    
2.      this.name = name;
    
3.      this.age = age;
    
4.      this.sex = sex;
    
5.      this.weight = weight;
    
6.      this.eat = function() {7.          console.log('吃吃吃!!!');
    
8.      }
    
9.  }
    
10.  Person.prototype.sayHi = function() {11.      console.log("你好帅呀!!!");
    
12.  };
    

14.  function Student(name, age, sex, weight, score) {
    
15.      // 借用构造函数
    
16.      Person.call(this, name, age, sex, weight);
    
17.      this.score = score;
    
18.  }
    
19.  var stu = new Student("小舞", 16, "女", "40kg", "100");
    
20.  console.log(stu.name, stu.age, stu.sex, stu.weight, stu.score);
    
21.  stu.eat();
    
22.  stu.sayHi(); // > 报错 stu.sayHi is not a function
    

无论是独自应用原型链继承,还是独自应用借用构造函数继承,都有很大的毛病,最好的方法是,将两者联合一起应用,施展各自的劣势,这就是咱们上面要讲的组合继承。

2.5 组合继承

原型继承和借用构造函数继承都存在各自的毛病,咱们能够将这二者联合到一起,从而施展二者之长。即在继承过程中,既能够保障每个实例都有它本人的属性,又能做到对一些属性和办法的复用。这时组合继承应运而生,组合继承 = 原型继承 + 借用构造函数继承。



1.  function Person(name, age, sex) {
    
2.      this.name = name;
    
3.      this.age = age;
    
4.      this.sex = sex;
    
5.  }
    
6.  Person.prototype.sayHi = function() {7.      console.log("你好帅呀!!!");
    
8.  };
    

10.  function Student(name, age, sex, score) {
    
11.      // 借用构造函数: 解决属性值反复的问题
    
12.      Person.call(this, name, age, sex);
    
13.      this.score = score;
    
14.  }
    
15.  // 扭转原型指向 --- 原型继承解决原型办法不能被继承问题
    
16.  Student.prototype = new Person(); // 不传值
    
17.  Student.prototype.eat = function() {18.      console.log("吃吃吃!!!");
    
19.  };
    
20.  var stu = new Student("小三", 16, "男", "111 分");
    
21.  console.log(stu.name, stu.age, stu.sex, stu.score);
    
22.  stu.sayHi();
    
23.  stu.eat();
    
24.  var stu2 = new Student("小舞", 15, "女", "1111 分");
    
25.  console.log(stu2.name, stu2.age, stu2.sex, stu2.score);
    
26.  stu2.sayHi();
    
27.  stu2.eat();
    

2.6 另一种继承形式:拷贝继承(for-in)

拷贝继承:把一个对象中的属性或者办法间接复制到另一个对象中。



1.  function Person() {}
    
2.  Person.prototype.name = "小三";
    
3.  Person.prototype.age = 18;
    
4.  Person.prototype.sex = "男";
    
5.  Person.prototype.height = 100;
    
6.  Person.prototype.play = function() {7.      console.log("玩的好开心呀!!!????");
    
8.  };
    
9.  var obj = {};
    
10.  // Person 中有原型 prototype,prototype 就是一个对象, 那么外面,name,age,sex,height,play 都是该对象中的属性或者办法
    
11.  // 新对象 obj 通过拷贝 Person 中原型 prototype 对象中的属性和办法继承 Person 中原型 prototype 对象的属性和办法
    
12.  for (var key in Person.prototype) {13.      obj[key] = Person.prototype[key];
    
14.  }
    
15.  console.dir(obj);
    
16.  obj.play();
    

3 总结

原型链是一种关系, 是实例对象和原型对象之间的关系, 这种关系是通过原型 (proto) 来分割的。继承是类与类之间的关系,js 不是面向对象的语言,没有类但能够通过函数模仿类,模仿面向对象中的继承。模仿继承是为了实现数据共享,节俭内存空间。
JS 中的继承形式:
1 原型继承: 通过扭转原型的指向实现继承。
2 借用构造函数继承: 次要解决属性反复的问题,会导致父类中的原型办法不能继承。
3 组合继承: 原型继承 + 借用构造函数继承,既能解决属性反复问题, 又能解决办法不能被继承的问题。
4 拷贝继承: 把对象中须要共享的属性或办法, 间接通过遍历的形式复制到另一个对象中。

小伙伴们明天的学习就到这里了,你能够应用明天学习的技巧来改善一下你已经的代码,如果想持续进步,欢送关注我,每天学习提高一点点,就是当先的开始。

退出移动版