从零开始学 Web 之 JS 高级(二)原型链,原型的继承
大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新......
- github:https://github.com/Daotin/Web
- 微信公众号:Web前端之巅
- 博客园:http://www.cnblogs.com/lvonve/
- CSDN:https://blog.csdn.net/lvonve/
在这里我会从 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); // trueconsole.log(Person.prototype.__proto__ === Object.prototype); // trueconsole.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); // Daotinconsole.log(stu.age); // 18stu.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 传过来,具体传多少,我自己指定。这样不同的子类,通过自己可以设置不同的属性。
缺陷2:stu.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 高级(二)原型链,原型的继承相关推荐
- 从零开始学 Web 系列教程
大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新-- github:https://github.com/Daotin/Web 微信公众号:前端队长 博客园:http://ww ...
- 从零开始学 Web 之 BOM(四)client系列
大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公众号:Web前端之巅 博客园:ht ...
- 从零开始学 Web 之 DOM(六)为元素绑定与解绑事件
大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新...... +-------------------------------------------------------- ...
- 从零开始学前端 - 16. JS对象Object介绍及常用方法
作者: 她不美却常驻我心 博客地址: https://blog.csdn.net/qq_39506551 微信公众号:老王的前端分享 每篇文章纯属个人经验观点,如有错误疏漏欢迎指正.转载请附带作者信息 ...
- 从零开始学 Web 之 HTML5(一)HTML5概述,语义化标签
大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公众号:Web前端之巅 博客园:ht ...
- 从零开始学 Web 之 ES6(三)ES6基础语法一
大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公众号:Web前端之巅 博客园:ht ...
- 从零开始学 Web 之 Ajax(七)跨域
大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公众号:Web前端之巅 博客园:ht ...
- 从零开始学 Web 之 JavaScript(三)函数
大家好,这里是「 Daotin的梦呓 」从零开始学 Web 系列教程.此文首发于「 Daotin的梦呓 」公众号,欢迎大家订阅关注.在这里我会从 Web 前端零基础开始,一步步学习 Web 相关的知识 ...
- 从零开始学 Web 之 移动Web(九)微金所案例
大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公众号:Web前端之巅 博客园:ht ...
最新文章
- [转]Photoshop Lab模式下保细节修复偏暗人物照片
- 【Java 泛型】泛型用法 ( 泛型类用法 | 泛型方法用法 | 泛型通配符 ? | 泛型安全检查 )
- [译]Front-end-Developer-Interview-Questions
- centos一键安装redmine
- springboot入门介绍
- anaconda3 安装tensorflow 报Cannot remove entries fro
- 营销心理学:如何挣女人的钱?
- java 数组 转set_java中的list,set,数组之间的转换
- 【c++ templates读书笔记】【4】技巧性基础知识
- 机器学习基础算法23-XGBoost实践
- mysql常用数据操作之增、删、改
- Ubuntu 实时显示网速
- 电脑显示your pc android,新版DeX模式不再强调手机变PC,而是让Android融合PC
- u盘文件不显示却占空间
- 原子范数最小化(Atomic Norm Minimization)
- 运维监控软件的选择对比----Zabbix vs Prometheus
- Z怎样写系统测试报告(A)
- 成为富人的十大心理特质
- 数组加链表实现hashMap代码
- Node.js Buffers缓存对象
热门文章
- [Java] 蓝桥杯ALGO-149 算法训练 5-2求指数
- [Python] L1-018. 大笨钟-PAT团体程序设计天梯赛GPLT
- 1025. 反转链表 (25)-浙大PAT乙级真题
- java设置只有一行表格,为什么我的表格插入一行后 样式都变了?是因为没有设置css吗?如果在java函数中插入的td.innerHTML = input type='text'/,可以设置样...
- JAVA加勒比_【JAVA】synchronized关键字
- 局域网共享设置软件_新页软件:如何设置局域网模式
- 一个maven错误:org/apache/maven/shared/filtering/MavenFilteringException
- linux vsftpd 550 create directory operation failed解决方法
- java拷贝构造函数
- 中国大数据人才薪酬报告