JavaScript 原型与继承
JavaScript 原型与继承
JavaScript 中函数原型是实现继承的基础。prototype、construct、原型链以及基于原型链的继承是面向对象的重要内容
prototype
原型即 prototype,是函数的一个属性,是一个对象
function Car() {}
console.log(typeof Car.prototype);
console.log(Car.prototype);
// object
// {...}
所有被构造函数构造出的对象都能访问 prototype 上定义的属性和方法
function Car() {
this.brand = "BMW";
this.color = "red";
}
Car.prototype.price = "100 万";
var car1 = new Car();
var car2 = new Car();
console.log(car1.price);
console.log(car2.price);
// 100 万
// 100 万
构造函数内部和 prototype 定义了同名属性,实例对象会优先调用构造函数中的属性
function Car() {
this.price = "10 万";
}
Car.prototype.price = "100 万";
var car1 = new Car();
console.log(car1.price);
// 10 万
通过实例对象不能更改 prototype 上的属性
function Car() {}
Car.prototype.price = "100 万";
var car1 = new Car();
car1.price = "10 万";
console.log(Car.prototype.price);
// 100 万
一般将不变化的内容或方法放在 prototype 下,需要动态变化的放在构造方法内,通过参数配置
constructor
constructor 指向构造函数本身
实例对象的 constructor 属性指向构造函数
function Car() {}
var car = new Car();
console.log(car.constructor);
console.log(Car)
// Car(){}
// Car(){}
constructor 可以被更改
constructor 可以被修改,但是并不会影响实例化对象
function Bike() {
this.name = "bike";
}
Bike.prototype.name = "Bike";
function Car() {}
Car.prototype = {
constructor: Bike
}
var car = new Car();
console.log(Car.prototype);
console.log(car.name);
// {constructor: Bike(){}, ...}
// undefined
__proto__
构造函数在实例化时,将其 prototype 挂载到函数内 this 的
__proto__下function Car() {}
Car.prototype.name = "Jett";
var car = new Car();
console.log(Car.prototype);
console.log(car.__proto__);
// Car.prototype ->
// {
// name: "Jett",
// construct: Car(){}
// _proto_: {...}
// }
// car._proto_ ->
// {
// name: "Jett",
// construct: Car(){}
// _proto_: {...}
// }
//
可以看到,打印出的 Car.prototype 和 car.
__proto__内容一致。因为在实例化对象时,Car.prototype 被挂载到函数内的 this.__proto__上,即实例对象的__proto__属性上prototype 是构造函数的属性,
__proto__属于每个实例对象的,是一个内部属性,它们指向相同的内容可以通过实例对象访问
__proto__属性,并对其进行修改function Car() {}
Car.prototype.name = 'BWM';
var car = new Car();
console.log(car.name);
car.__proto__= {
name:"Benz"
}
console.log(car.name);
// BWM
// Benz
也可以更改 prototype 的属性到达效果
function Car() {}
Car.prototype.name = 'BWM';
var car = new Car();
console.log(car.name);
Car.prototype.name = 'Benz';
console.log(car.name);
// BWM
// Benz
但是,将 prototype 重新赋值并不能对之前实例化的对象造成影响
function Car() {}
Car.prototype.name = 'BWM';
var car = new Car();
console.log(car.name);
Car.prototype = {
name: "Benz"
}
console.log(car.name);
// BWM
// BWM
这是因为重新赋值相当于创建新对象,使 prototype 指向的新的对象,而实例对象的
__proto__属性依然指向原来的内容,相当于一个对象的两个引用,其中一个不在指向该对象,而且指向了新对象这不能对已经实例化出的对象造成影响,但是后面再实例化对象则可以造成影响,因为实例化过程中将修改后的 prototype 挂载到了实例对象的
__proto__属性下,二者指向同一对象
原型链
prototype 中的
__proto__属性function Car() {}
var car = new Car();
console.log(Car.prototype);
当我们打印构造函数的 prototype 属性时,可以看到
{
constructor: Car(),
__proto__: {...}
}
prototype 中也有
__proto__属性,实例化过程 protorype 被挂载到实例对象的__proto__下,这就意味着实例对象的__proto__中也有一个__proto__属性因为这里的 prototype 是一个非空对象,是由 new Object() 或者其他自定义构造方法实例化出的,自然也有
__proto__属性链式的
__proto__原型链是由
__proto__组成的链接,原型链的顶端是 Object.prototypeJuniorCoder.prototype.basicSkill = "html/css";
function JuniorCoder() {
this.lowerSkill = "javascript"
}
var junior = new JuniorCoder();
SeniorCoder.prototype = junior;
function SeniorCoder() {
this.advancedSkill = "vue";
}
var senior = new SeniorCoder();
console.log(senior);
这里将 JuniorCoder() 的实例对象赋值给 SeniorCoder.prototype,打印出
SeniorCoder {
advcedSkill: "vue",
__proto__: { // senior.__proto__ ,即 SeniorCoder.protoype
lowerSkill: "javascript",
__proto__: { // junior.__proto__ ,即 JuniorCoder.prototype
basicSkill: "html/css",
__proto__: { // Object.prototype
constructor: Object(),
toString: toString()
// ...
}
}
}
}
可以看出,senior 的
__proto__属性指向 JuniorCoder() 实例 junior,这是因为之前 将 junior 赋值给了 SeniorCoder.prototype此外,junior 的
__proto__也指向了一个对象,这个对象就是 JuniorCoder.porotype,相当于 new Object() 得出的,所以 junior 的__proto__下的__proto__就是 Object.prototype,这就是原型链的顶端,在里面我们还可以看到 toString 方法等等访问原型链上属性
JuniorCoder.prototype.basicSkill = "html/css";
JuniorCoder.prototype.sex = "man";
function JuniorCoder() {
this.lowerSkill = "javascript"
this.age = 22;
}
var junior = new JuniorCoder();
SeniorCoder.prototype = junior;
function SeniorCoder() {
this.advancedSkill = "vue";
}
var senior = new SeniorCoder();
console.log(senior.age);
console.log(senior.sex);
// 22
// man
senior 可以访问 junior 本身的属性,也可以访问 JuniorCoder.prototype 上的属性,因为 junior 被挂载到了 SeniorCoder.prototype 上
JuniorCoder.prototype.basicSkill = "html/css";
function JuniorCoder() {
this.lowerSkill = "javascript";
this.years = 3;
}
var junior = new JuniorCoder();
SeniorCoder.prototype = junior;
function SeniorCoder() {
this.advancedSkill = "vue";
}
var senior = new SeniorCoder();
senior.years++;
// 等同于 senior.years = senior.years + 1;
console.log(senior.years);
console.log(junior.years);
// 4
// 3
可以看到,通过 senior 试图改变 years 属性并不能真正影响 junior 的 years 属性,实际上只是在 senior 下创建了新的 years 属性,并将 junior.years 加一的结果赋值给它
Object.creat()
Object 的 creat 方法用于创建对象,参数指定 prototype,可以为对象或 null
var test = {
name: "obj"
}
var obj = Object.create(test);
console.log(obj.name);
console.log(obj.__proto__ == test);
// obj
// true
Object.creat(null)
var obj = Object.create(null);
console.log(obj);
document.write(obj);
// {}
// 报错
控制台显示 obj 是一个空对象,没有任何属性,包括
__proto__,如果使用 document.write(obj) 则会报错,因为 document.write 方法会把参数转成字符串再打印在页面,默认调用 toString() 方法,toString 方法需要从原型链上继承而来,而 obj 是一个完全的空对象,没有原型链,也没有 toString 方法,所以会报错
基于原型的继承
利用原型链实现继承
JuniorCoder.prototype.basicSkill = "html/css";
function JuniorCoder() {
this.lowerSkill = "javascript"
this.age = 22;
}
var junior = new JuniorCoder();
SeniorCoder.prototype = junior;
function SeniorCoder() {
this.advancedSkill = "vue";
}
var senior = new SeniorCoder();
senior 继承了 junior 的自身属性及原型链
call/apply 实现继承
function JuniorCoder(lowerSkill) {
this.lowerSkill = lowerSkill;
}
function SeniorCoder(lowerSkill, advancedSkill) {
JuniorCoder.apply(this, [lowerSkill]);
this.advancedSkill = advancedSkill;
}
var senior = new SeniorCoder("javascript", "vue");
继承了 JuniorCoder 实例的自身属性,不能继承原型链
公共原型继承
JuniorCoder.prototype.basicSkill = "html/css";
function JuniorCoder() {
this.lowerSkill = "javascript"
}
SeniorCoder.prototype = JuniorCoder.prototype;
function SeniorCoder() {
this.advancedSkill = "vue";
}
var senior = new SeniorCoder();
senior 继承 JuniorCoder 实例的原型链,不继承自身属性,但是改动 SeniorCoder.prototype 会影响 JuniorCoder.prototype
中间对象继承(圣杯模式)
JuniorCoder.prototype.basicSkill = "html/css";
function JuniorCoder() {
this.lowerSkill = "javascript"
}
Buffer.prototype = JuniorCoder.prototype;
function Buffer() {}
SeniorCoder.prototype = new Buffer();
function SeniorCoder() {
this.advancedSkill = "vue";
}
SeniorCoder.prototype.basicSkill = "markdown";
console.log(SeniorCoder.prototype.basicSkill);
console.log(JuniorCoder.prototype.basicSkill);
// markdown
// html/css
继承原型链,不继承自身属性,prototype 不相互影响,这种继承方式更为实用
进行封装以后,更适应企业级开发
JuniorCoder.prototype.basicSkill = "html/css"; function JuniorCoder() {
this.lowerSkill = "javascript"
} function SeniorCoder() {
this.advancedSkill = "vue";
}
inherit(SeniorCoder, JuniorCoder);
SeniorCoder.prototype.basicSkill = "markdown";
console.log(new SeniorCoder());
console.log(new JuniorCoder()); function inherit(Target, Origin) {
Target.prototype = Object.create(Origin.prototype);
Target.prototype.constructor = Target;
Target.prototype.superClass = Origin;
}
使用 Object 的 creat 方法直接创建中间对象,将 construtor、superClass 属性设置好,便于分析和维护
hasOwnProperty()
判断属性是否是实例对象本身的,如果是则返回 true
Car.prototype.brand = "BMW";
function Car() {
this.color = "red";
}
var car = new Car();
console.log(car.hasOwnProperty("brand"));
console.log(car.hasOwnProperty("color"));
// false
// true
instanceOf
判断实例对象的原型链上是否有某个构造方法
JuniorCoder.prototype.basicSkill = "html/css";
function JuniorCoder() {
this.lowerSkill = "javascript"
}
function SeniorCoder() {
this.advancedSkill = "vue";
}
inherit(SeniorCoder, JuniorCoder);
function inherit(Target, Origin) {
Target.prototype = Object.create(Origin.prototype);
Target.prototype.constructor = Target;
Target.prototype.superClass = Origin;
}
var senior = new SeniorCoder();
console.log(senior instanceof SeniorCoder);
console.log(senior instanceof JuniorCoder);
console.log(senior instanceof Object);
// true
// true
// true
JavaScript 原型与继承的更多相关文章
- 深入理解:JavaScript原型与继承
深入理解:JavaScript原型与继承 看过不少书籍,不少文章,对于原型与继承的说明基本上让人不明觉厉,特别是对于习惯了面向对象编程的人来说更难理解,这里我就给大家说说我的理解. 首先JavaScr ...
- JavaScript原型与继承
JavaScript原型与继承 原型 在JavaScript中,每个函数都有一个prototype属性,这个属性是一个指针,指向该函数的原型对象.这个原型对象为所有该实例所共享.在默认情况下,原型对象 ...
- JavaScript原型与继承的秘密
在GitHub上看到的关于JavaScript原型与继承的讲解,感觉很有用,为方便以后阅读,copy到自己的随笔中. 原文地址:https://github.com/dreamapplehappy/b ...
- javascript原型链继承
一.关于javascript原型的基本概念: prototype属性:每个函数都一个prototype属性,这个属性指向函数的原型对象.原型对象主要用于共享实例中所包含的的属性和方法. constru ...
- JavaScript 原型与继承机制详解
引言 初识 JavaScript 对象的时候,我以为 JS 是没有继承这种说法的,虽说 JS 是一门面向对象语言,可是面向对象的一些特性在 JS 中并不存在(比如多态,不过严格来说也没有继承).这就困 ...
- 8条规则图解JavaScript原型链继承原理
原形链是JS难点之一,而且很多书都喜欢用一大堆的文字解释给你听什么什么是原型链,就算有图配上讲解,有的图也是点到为止,很难让人不产生疑惑. 我们先来看一段程序,友情提示sublimeText看更爽: ...
- 【Javascript】Javascript原型与继承
一切都是对象! 以下的四种(undefined, number, string, boolean)属于简单的值类型,不是对象.剩下的几种情况——函数.数组.对象.null.new Number(10) ...
- 【前端知识体系-JS相关】深入理解JavaScript原型(继承)和原型链
1. Javascript继承 1.1 原型链继承 function Parent() { this.name = 'zhangsan'; this.children = ['A', 'B', 'C' ...
- JavaScript原型及继承
一.浅谈原型 首先我们要知道创建对象的方法有两种: 1.通过字面量的方式直接创建 var obj = { name:'baimao', age:21 } 2.通过构造函数创建对象 function P ...
随机推荐
- OpenSSL之X509系列
OpenSSL之X509系列之1---引言和X509概述 [引言] X509是系列的函数在我们开发与PKI相关的应用的时候我们都会用到,但是OpenSSL中对X509的描述并不是很多,鉴于些,我 ...
- 吴裕雄--天生自然 R语言开发学习:使用ggplot2进行高级绘图(续二)
#----------------------------------------------------------# # R in Action (2nd ed): Chapter 19 # # ...
- DBCP + C3P0 连接池
DBCP 配置参数如下: 参数 默认值 可选值 格式 说明 username 用户名 password 密码 url 数据库连接url driverClassNam ...
- 算法设计与分析-Week12
题目描述 You are given coins of different denominations and a total amount of money amount. Write a func ...
- lsync+rsync 实时同步(ubuntu16.04系统)
1.同步端需要安装 lsync/rsyncapt-get install lsyncd rsync2.生成ssh公钥,粘贴到目标机器里面3.创建配置文件mkdir /etc/lsyncdcat /et ...
- 查漏补缺:QT入门
1.什么世QT Qt是一个跨平台的C++图形用户界面应用程序框架,为应用程序开发者提供建立艺术级图形界面所需的所有功能.它是完全面向对象的,容易扩展,并且允许真正的组建编程. 2.支持平台 Windo ...
- HTC新机A9足以取代iPhone 你相信吗?
你相信吗?" title="HTC新机A9足以取代iPhone 你相信吗?"> 自信和嘴硬之间,往往就是成功与失败的分水岭.乔老爷子当年推出iPhone.iPad等 ...
- OPPO招聘-互联网测试
邮 箱:ljy@oppo.com 工作地点:深圳
- PHP正则表达式-修饰符
我们在PHP正则表达式的学习中会碰到修饰符,那么关于PHP正则表达式修饰符的理解以及使用我们需要注意什么呢?那么我们来具体的看看它的概念以及相关内容.在学习PHP正则表达式修饰符之前先来理解下贪婪模式 ...
- 第二章 表与指针Pro SQL Server Internal (Dmitri Korotkev)
聚集索引 聚集索引就是表中数据的物理顺序,它是按照聚集索引分类的.表只能定义一个聚集索引. 如果你要在一个有数据的堆表中创建一个聚集索引,如2-5所示,第一步要做的就是SQL服务器创建另一个根据聚集索 ...