基础1:JS的原型和原型链究竟
JS的原型和原型链究竟是什么?
1. 从JS创建一个对象开始说起:
1.1 工厂模式创建对象 (缺点是无法知道创建出来的对象是一个什么类型的对象)
function createPerson(name, age, job) {
let o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function() {
console.log(this.name);
};
return o;
}
let person1 = createPerson("Nicholas", 29, "Software Engineer");
let person2 = createPerson("Greg", 27, "Doctor");
1.2 构造函数创建对象
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function() {
console.log(this.name);
};
}
let person1 = new Person("Nicholas", 29, "Software Engineer");
let person2 = new Person("Greg", 27, "Doctor");
person1.sayName(); // Nicholas
person2.sayName(); // Greg
console.log(person1.constructor == Person); // true
console.log(person2.constructor == Person); // true
console.log(person1 instanceof Person); // true
console.log(person2 instanceof Person); // true
console.log(person1.sayName == person2.sayName); // false
优点:没有显示创建对象,只有在new 的时候在内存里创建一个新对象;person1 和person2都是Person的实例,相比于工厂模式,定义自定义构造函数可以确保实例被标识为特定类型。
缺点:sayName方法在每次创建实例的时候都需要实例化一遍
// 可以这样解决上面的缺点问题,但是这样做的后果,就是污染了全局作用域
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = sayName;
}
function sayName() {
console.log(this.name);
}
let person1 = new Person("Nicholas", 29, "Software Engineer");
let person2 = new Person("Greg", 27, "Doctor");
person1.sayName(); // Nicholas
person2.sayName(); // Greg
2. 原型模式
每个函数都会创建一个prototype属性,这个是属性一个对象,是通过构造函数创建的对象的原型。使用原型对象的好处是,在它上面定义的属性和方法可以被对象实例共享
function Person() {}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function() {
console.log(this.name);
};
let person1 = new Person();
person1.sayName(); // "Nicholas"
let person2 = new Person();
person2.sayName(); // "Nicholas"
console.log(person1.sayName == person2.sayName); // true
console.log(Person.prototype.__proto__) // Object.prototype
console.log(Person.prototype.__proto__.constructor) // Object
console.log(Person.prototype.__proto__.__proto__) // null
2.1 理解原型
js在任何情况下,只要创建一个函数就会按照特定的规则为这个函数创建一个 prototype 属性(指向原型对象),所有原型对象默认会自动获得一个constructor属性,这个属性指回构造函数。理解原型的根本就是理解原型对象。
Person.prototype.constructor = Person
构造函数和原型对象之间可以用prototype和constructor来进行指向的,那实例和原型对象通过什么联系的呢。每个构造函数的实例的内部都有一个[[Prototype]]指针,这个指针被赋值为构造函数的原型对象,js中没有访问这个[[Prototype]]特性的标准方式,有些浏览器厂商会在每个对象上暴露__proto__属性,通过这个属性可以访问对象的原型。
注意:实例与构造函数原型之间有直接的联系,但实例与构造函数之间没有
function Person() {}
/**
* 声明之后,构造函数就有了一个
* 与之关联的原型对象:
*/
console.log(typeof Person.prototype);
console.log(Person.prototype);
实例通过"proto"链接到原型对象,它实际上指向隐藏特性[[Prototype]],构造函数通过 prototype 属性链接到原型对象。同一个构造函数创建的两个实例共享同一个原型对象。
console.log(person1.__proto__ === Person.prototype); // true
conosle.log(person1.__proto__.constructor === Person); // true
/**
* 同一个构造函数创建的两个实例
* 共享同一个原型对象:
*/
console.log(person1.__proto__ === person2.__proto__); // true

3. 原型链继承
js实现继承只能支持实现继承,不支持接口继承,这些因为js函数没有签名
function SuperType() {
this.property = true;
}
SuperType.prototype.getSuperValue = function() {
return this.property;
};
function SubType() {
this.subproperty = false;
}
// 继承 SuperType
let n
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function () {
return this.subproperty;
};
let instance = new SubType();
console.log(instance.getSuperValue()); // true
4. Funtion.proto === Function.prototype
我们知道,
Funtion.__proto__ === Function.prototype是成立的,那么就有了经典的“鸡生蛋还是蛋生鸡的问题”。
Function.prototype 对象是一个函数(对象),其 [[Prototype]] 内部属性值指向内建对象 Object.prototype。Function.prototype 对象自身没有 valueOf 属性,其从 Object.prototype 对象继承了valueOf 属性。
“所有的函数都有prototype属性”。这句话其实是错误的。Function.prototype就是一个函数,但是这个函数并没有prototype属性。
let o = {a: 1};
// 原型链: o ---> Object.prototype ---> null
let a = ["yo", "whadup", "?"];
// 原型链: a ---> Array.prototype ---> Object.prototype ---> null
function f(){
return 2;
}
// 原型链: f ---> Function.prototype ---> Object.prototype ---> null
let fun = new Function();
// 原型链: fun ---> Function.prototype ---> Object.prototype ---> null
function Foo() {}
let foo = new Foo();
// 原型链: foo ---> Foo.prototype ---> Object.prototype ---> null
function Foo() {
return {};
}
let foo = new Foo();
// 原型链: foo ---> Object.prototype ---> null
Function 构造函数是一个函数对象,其 [[Class]] 属性是 Function。Function 的 [[Prototype]] 属性指向了 Function.prototype,即
Function.__proto__ === Function.prototype
// true

到这里就有意思了,我们看下鸡生蛋蛋生鸡问题。
我们看下面这段代码:
Object instanceof Function // true
Function instanceof Object // true
Object instanceof Object // true
Function instanceof Function // true
Object 构造函数继承了 Function.prototype,同时 Function 构造函数继承了Object.prototype。这里就产生了 鸡和蛋 的问题。为什么会出现这种问题,因为 Function.prototype 和 Function.proto 都指向 Function.prototype。
// Object instanceof Function 即
Object.__proto__ === Function.prototype // true
// Function instanceof Object 即
Function.__proto__.__proto__ === Object.prototype // true
// Object instanceof Object 即
Object.__proto__.__proto__ === Object.prototype // true
// Function instanceof Function 即
Function.__proto__ === Function.prototype // true
个人理解如下:
Function 是 built-in 的对象,也就是并不存在“Function对象由Function构造函数创建”这样显然会造成鸡生蛋蛋生鸡的问题。实际上,当你直接写一个函数时(如 function f() {} 或 x => x),也不存在调用 Function 构造器,只有在显式调用 Function 构造器时(如 new Function('x', 'return x') )才有。即先有 Function.prototype 然后有的 function Function() ,所以就不存在鸡生蛋蛋生鸡问题了,把 Function.proto 指向 Function.prototype 是为了保证原型链的完整,让 Function 可以获取定义在 Object.prototype 上的方法。

2022-03-01 额外总结
- 每个函数都有一个prototype属性(除了Function.prototype函数),这个属性指向原型对象,这个对象正是调用该构造函数创建的实例的原型;
- 每一个js对象(null除外)在创建的时候都会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型“继承”属性;
- 每一个实例对象都具有一个属性,
__proto__,这个属性会指向该对象的原型。
基础1:JS的原型和原型链究竟的更多相关文章
- 前端总结·基础篇·JS(一)原型、原型链、构造函数和字符串(String)
前端总结系列 前端总结·基础篇·CSS(一)布局 前端总结·基础篇·CSS(二)视觉 前端总结·基础篇·CSS(三)补充 前端总结·基础篇·JS(一)原型.原型链.构造函数和字符串(String) 前 ...
- 前端总结·基础篇·JS(一)五大数据类型之字符串(String)
前端总结系列 前端总结·基础篇·CSS(一)布局 前端总结·基础篇·CSS(二)视觉 前端总结·基础篇·CSS(二)补充 前端总结·基础篇·JS(一)五大数据类型之字符串(String) 目录 这是& ...
- 前端总结·基础篇·JS(二)数组深拷贝、去重以及字符串反序和数组(Array)
目录 这是<前端总结·基础篇·JS>系列的第二篇,主要总结一下JS数组的使用.技巧以及常用方法. 一.数组使用 1.1 定义数组 1.2 使用数组 1.3 类型检测 二.常用技巧 2.1 ...
- 前端总结·基础篇·JS(三)arguments、callee、call、apply、bind及函数封装和构造函数
前端总结系列 前端总结·基础篇·CSS(一)布局 前端总结·基础篇·CSS(二)视觉 前端总结·基础篇·CSS(三)补充 前端总结·基础篇·JS(一)原型.原型链.构造函数和字符串(String) 前 ...
- 前端总结·基础篇·JS(四)异步请求及跨域方案
前端总结系列 前端总结·基础篇·CSS(一)布局 前端总结·基础篇·CSS(二)视觉 前端总结·基础篇·CSS(三)补充 前端总结·基础篇·JS(一)原型.原型链.构造函数和字符串(String) 前 ...
- JS基础-该如何理解原型、原型链?
JS的原型.原型链一直是比较难理解的内容,不少初学者甚至有一定经验的老鸟都不一定能完全说清楚,更多的"很可能"是一知半解,而这部分内容又是JS的核心内容,想要技术进阶的话肯定不能对 ...
- 总结一下js的原型和原型链
最近学习了js的面向对象编程,原型和原型链这块是个难点,理解的不是很透彻,这里搜集了一些这方面的资料,以备复习所用 一. 原型与构造函数 Js所有的函数都有一个prototype属性,这个属性引用了一 ...
- JS三座大山再学习(一、原型和原型链)
原文地址 ## 前言 西瓜君之前学习了JS的基础知识与三座大山,但之后工作中没怎么用,印象不太深刻,这次打算再重学一下,打牢基础.冲鸭~~ 原型模式 JS实现继承的方式是通过原型和原型链实现的,JS中 ...
- JS三座大山再学习 ---- 原型和原型链
本文已发布在西瓜君的个人博客,原文传送门 ## 前言 西瓜君之前学习了JS的基础知识与三座大山,但之后工作中没怎么用,印象不太深刻,这次打算再重学一下,打牢基础.冲鸭~~ 原型模式 JS实现继承的方式 ...
随机推荐
- Spring cloud gateway 如何在路由时进行负载均衡
本文为博主原创,转载请注明出处: 1.spring cloud gateway 配置路由 在网关模块的配置文件中配置路由: spring: cloud: gateway: routes: - id: ...
- XDEBUG 选项
到官网 http://www.xdebug.com/download.php 下载 找到对应PHP版本的 Xdebug ,后面带 TS 的为线程安全,本机环境为 win7 64 + php-5.5.1 ...
- TDSQL|三篇论文入选国际顶会SIGMOD,厉害了腾讯云数据库
好消息!6月13日,腾讯云数据库三篇论文再次入选数据库行业顶会SIGMOD,被SIGMOD 2022 Research Full Paper(研究类长文)收录. 本次被收录的研究成果中,新型数据结构设 ...
- JS:条件语句1
条件语句: 1.if...else if (condition1) { 当条件 1 为 true 时执行 } else { 当条件 1 不为 true 时执行 } if (condition1) { ...
- robotframework之环境安装
一.安装python2.7环境,python --version查询python安装的版本 二.setuptools安装
- 腾讯云原生数据库TDSQL-C入选信通院《云原生产品目录》
近日,中国信通院.云计算开源产业联盟正式对外发布<云原生产品目录>,腾讯云原生数据库TDSQL-C凭借其超强性能.极致效率的弹性伸缩和完善的产品化解决方案体系,成功入围目录. 全球数字经济 ...
- 手写一个模拟的ReentrantLock
package cn.daheww.demo.juc.reentrylock; import sun.misc.Unsafe; import java.lang.reflect.Field; impo ...
- 使用xpath查找元素的子元素,找不到
List<WebElement> allUserList = driver.findElements(By.xpath("xxx")); for (WebElement ...
- HashMap中的哈希函数分析
首先我们要知道,在理想情况下的哈希表中,哈希函数生成的哈希值是value在数组中的下标,其范围是分布于负无穷到正无穷的整个实整数轴的.而在现实情况下,是不可能存在这么大的一个数组的.接下来分析Hash ...
- led跑马灯多种方法(移位法,位拼接法,调用模块法,位移及位拼接语法,testbench的理解,源文件的存储路径,计数器的个数,调用模块的方式)
跟着教程写了几种方法,才发现自己写的虽然能实现,但比较繁琐.教程有三种方法: 1.移位法,每次左移一位,相比我自己写的,优点是不用把每一种情况都写出来.但是需要考虑左移到最后一位时需要自己再写个赋值语 ...