大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新......

在这里我会从 Web 前端零基础开始,一步步学习 Web 相关的知识点,期间也会分享一些好玩的项目。现在就让我们一起进入 Web 前端学习的冒险之旅吧!

一、原型链

原型链表示的是实例对象与原型对象之间的一种关系,这种关系是通过__proto__原型来联系的。

1、原型的指向改变

实例对象的原型 __proto__ 指向的是该对象的构造函数中的原型对象 prototype,如果该对象的构造函数的 prototype 指向改变了,那么实例对象中的原型 __proto__ 的指向也会跟着改变。

例如:

Person.prototype = new Cat();

因为 Person.prototype = {}; 可以是一个对象,所以传入另一个对象的实例函数是可以的,这时候 Person 的实例对象可以访问 Cat 原型 prototype 中的属性和方法,而不能再访问自己 Person 中原型的属性和方法了。

2、原型链的最终指向

实例对象的__proto__指向的是构造函数的原型对象 prototype,由于prototype也是个对象,所以也有 __proto__ ,这个 __proto__ 指向的是 Object 的 prototype,而 Object 的 prototype 里面的 __proto__ 指向的是 null。

示例:

function Person() {}
var per = new Person(); console.log(per.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__); // null

原型链图示:

3、原型指向改变后添加原型方法

先看个案例:问下面程序有问题吗?

function Person() {}
Person.prototype.eat = function () {};
function Student() {}
Student.prototype.say = function () {};
Student.prototype = new Person(); var stu = new Student();
stu.say();

解答:stu.say(); 会报错。因为 Student 的原型指向变成了 Person 的一个实例对象,Person 的实例对象钟并没有 say 方法,所以报错。

解决办法:在原型指向改变之后再添加原型方法。

function Person() {}
Person.prototype.eat = function () {};
function Student() {} Student.prototype = new Person();
Student.prototype.say = function () {}; var stu = new Student();
stu.say();

PS:这个时候就不会报错, Student 添加的原型方法的位置是一个匿名 Person 的实例对象中,这里是一个 Person 的实例对象,不是所有的,所以当你再 new 一个 Person 的实例对象的时候,不会有 say 方法。

4、实例对象和原型对象属性重名问题

当实例对象访问一个属性的时候,会先从实例对象中找,找到了直接使用,找不到再到指向的原型对象中找,找到了使用,还是找不到,则为 undefined。

如何改变原型对象中的属性的值呢?怎么赋值的怎么修改。

如果你使用 对象.属性 = 值 的方式来赋值的话,如果这个属性在实例对象中有的话,改变的是实例对象中属性的值;如果实例对象中没有这个属性的话,则这次修改相当于给该实例对象添加了一个属性,其指向的原型对象中相应的属性的值并没有被改变。


二、原型的继承

1、原型的继承

原型的第二个作用:继承。目的也是节省内存空间。

通过改变子类原型的指向到父类的实例对象,可以实现继承。

案例:

 // 父类:人
function Person(name, age) {
this.name= name;
this.age=age;
}
Person.prototype.eat = function () {
console.log("eat()");
}; // 子类:学生
function Student(sex) {
this.sex=sex;
}
// 子类继承父类只需要改变子类原型的指向到父类的实例对象。
Student.prototype = new Person("Daotin", 18);
Student.prototype.study = function () {
console.log("study()");
}; var stu = new Student("male");
console.log(stu.name); // Daotin
console.log(stu.age); // 18
stu.eat(); // eat()

缺陷1:在改变子类原型对象的指向的时候,属性在初始化的时候就固定了,那么每个子类实例对象的值都固定了。

解决办法:不需要子类原型的指向到父类的实例对象,只需要借用父类的构造函数。

    function Person(name, age) {
this.name = name;
this.age = age;
} Person.prototype.eat = function () {
console.log("eat()");
}; function Student(name, age, sex) {
Person.call(this, name, age);
this.sex = sex;
} //Student.prototype = new Person("Daotin", 18);
Student.prototype.study = function () {
console.log("study()");
}; var stu = new Student("Daotin", 18, "male");
console.log(stu.name);
console.log(stu.age);
console.log(stu.sex);
stu.eat(); // 不能访问

Person.call(this, name, age); 第一个参数 this,表示当前对象,意思是当前对象呼叫 Person,将 name 和 age 传过来,具体传多少,我自己指定。这样不同的子类,通过自己可以设置不同的属性。

缺陷2stu.eat(); 不能访问了,就是父类原型方法不能继承了。

解决办法组合继承(原型方式继承 + 借用构造函数继承)

    function Person(name, age) {
this.name = name;
this.age = age;
} Person.prototype.eat = function () {
console.log("eat()");
}; function Student(name, age, sex) {
Person.call(this, name, age); // 借用父类构造函数,实现父类属性的继承
this.sex = sex;
} Student.prototype = new Person(); // 不传参数了,实现原型方法的继承
Student.prototype.study = function () {
console.log("study()");
}; var stu = new Student("Daotin", 18, "male");
console.log(stu.name);
console.log(stu.age);
console.log(stu.sex);
stu.eat();
stu.study();

Student.prototype = new Person(); // 不传参数了,实现原型方法的继承。

Person.call(this, name, age); // 借用父类构造函数,实现父类属性的继承。

2、拷贝继承

就是把对象中需要共享的属性和方法直接以遍历的方式复制到了另一个对象中。

    function Person(name, age) {
this.name = name;
this.age = age;
} Person.prototype.eat = function () {
console.log("eat()");
}; var per = {}; // 循环拷贝
for(var key in Person.prototype) {
per[key] = Person.prototype[key];
}
console.log(per);

三、复习

1、函数的声明和函数表达式的区别

// 函数的声明
if(true) {
function f1() {
console.log("f1()--1");
}
} else {
function f1() {
console.log("f1()--2");
}
}
f1();

函数声明如果放在 if-else- 里面,chrome 和 firefox 输出 f1()--1,IE8 下输出 f1()--2,因为函数声明会提前,第二个将第一个覆盖了。

// 函数表达式
var func;
if(true) {
func = function () {
console.log("f1()--1");
};
} else {
func = function () {
console.log("f1()--2");
};
}
func();

函数表达式的方式,输出都是 f1()--1。所以尽量使用函数表达式。

2、严格模式

function func() {
console.log(this) // window
}
func();

正常情况下是证正确的。

"use strict";
function func() {
console.log(this) // window
}
window.func(); // 严格模式下必须加 window,因为他认为函数是一个方法,方法必须通过对象来调用的。

2.1、函数也是对象,对象不一定是函数(比如:Math)。

只要有 __proto__ 的就是对象;

只有要 prototype 的就是函数,因为函数才会调用 prototype 属性。

对象不一定是函数:比如 Math,中有 __proto__ ,但是没有 prototype

2.2、所有的函数都是由 Function 构造函数创建的实例对象。

既然函数是对象,那么是什么构造函数创建的呢?

var f1 = new Function("a", "b", "return a+b");
f1(1,2); // 上面相当于:函数的声明
function f1(a, b) {
return a+b;
}
f1(1,2); // 相当于:函数表达式
var f1 = function (a, b) {
return a+b;
}
f1(1,2);

那么 Function 是个什么东西呢?

经查看是对象也是函数?然后查看它的 __proto__ 指向的是 Object的原型对象。所有的对象指向的都是Object的原型对象。

从零开始学 Web 之 JS 高级(二)原型链,原型的继承的更多相关文章

  1. 从零开始学 Web 之 JavaScript 高级(一)原型,贪吃蛇案例

    大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公众号:Web前端之巅 博客园:ht ...

  2. 从零开始学 Web 之 JS 高级(三)apply与call,bind,闭包和沙箱

    大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公众号:Web前端之巅 博客园:ht ...

  3. 从零开始学 Web 之 ES6(二)ES5的一些扩展

    大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公众号:Web前端之巅 博客园:ht ...

  4. 从零开始学Web之HTML(二)标签、超链接、特殊符号、列表、音乐、滚动、head等

    大家好,这里是 Daotin 从零开始学 Web 系列教程.此文首发于「 Daotin的梦呓 」,欢迎大家订阅关注.在这里我会从 Web 前端零基础开始,一步步学习 Web 相关的知识点,期间也会分享 ...

  5. 从零开始学 Web 之 DOM(二)对样式的操作,获取元素的方式

    大家好,这里是「 Daotin的梦呓 」从零开始学 Web 系列教程.此文首发于「 Daotin的梦呓 」公众号,欢迎大家订阅关注.在这里我会从 Web 前端零基础开始,一步步学习 Web 相关的知识 ...

  6. 从零开始学 Web 之 BOM(二)定时器

    大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公众号:Web前端之巅 博客园:ht ...

  7. 从零开始学 Web 之 Ajax(二)PHP基础语法

    大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公众号:Web前端之巅 博客园:ht ...

  8. 从零开始学 Web 之 CSS(二)文本、标签、特性

    大家好,这里是「 Daotin的梦呓 」从零开始学 Web 系列教程.此文首发于「 Daotin的梦呓 」公众号,欢迎大家订阅关注.在这里我会从 Web 前端零基础开始,一步步学习 Web 相关的知识 ...

  9. 从零开始学 Web 之 JavaScript(二)变量

    大家好,这里是「 Daotin的梦呓 」从零开始学 Web 系列教程.此文首发于「 Daotin的梦呓 」公众号,欢迎大家订阅关注.在这里我会从 Web 前端零基础开始,一步步学习 Web 相关的知识 ...

随机推荐

  1. django 在centos 7 下 指定ip地址和端口 报错问题

    windows environment: python manage.py runserver host:port centos environment: python manage.py runse ...

  2. error while loading shared libraries的解決方法

    我是在启动nginx的时候报这个错误,搜索这个错误时发现这篇文章,非本人(小渡博客)原创. 原文地址:http://blog.csdn.net/dumeifang/article/details/29 ...

  3. MySQL配置文件优化(Innodb)

    Innodb配置文件参数调优 ——MySQL建议采用MySQL 5.6 InnoDB存储引擎 1.内存利用方面: innodb_buffer_pool_size ☆☆☆☆☆ Innodb优化首要参数. ...

  4. DOM追加笔记

    根据 W3C 的 HTML DOM 标准,HTML 文档中的所有内容都是节点: 整个文档是一个文档节点 每个 HTML 元素是元素节点 HTML 元素内的文本是文本节点 每个 HTML 属性是属性节点 ...

  5. Selenium webdriver操作日历控件

    一般的日期控件都是input标签下弹出来的,如果使用webdriver 去设置日期, 1. 定位到该input 2. 使用sendKeys 方法 比如:使用定位: driver.findElement ...

  6. 6.8 lambda方法 6.9 枚举类

    6.9枚举 对于对象个数有限的类可以用枚举类来避免创建对象的随意性. 枚举类关键字enum,是一种特殊的类:构造器只能用private修饰,枚举类的所有实例在第一行举出(系统默认用public sta ...

  7. Python开发——4.集合和字符串拼接

    一.集合(set) 1.集合的特性: 不同元素组成.元素是无序排列的可hash值 2.集合转为列表 s1 = {11,"hechouzi",(11,22,33)} names = ...

  8. RS232、RS485和RS422

    一.232电平.TTL电平和CMOS电平        1.232电平:逻辑1:-3V--15V: 逻辑0:+3-+15V. 2.TTL电平:逻辑1:5V: 逻辑0:0V.具体是,输出高电平:VOH≥ ...

  9. 第一个SpringBoot应用

    第一个SpringBoot应用 新建eclipse项目 编写pom文件,配置maven导入的springboot的jar包 <?xml version="1.0" encod ...

  10. azkaban disable 停用部分工作流

    在使用azkaban作为调度工具的时候,难免遇到只需要跑工作流某部分的情况,这时需要用到停用部分工作的操作, 如图: