深入理解javascript原型和原型链
文章目录
- 构造函数
- 1. new一个新对象的过程,发生了什么?
- 2. 手写new函数
- 3. 构造函数上的方法
- 原型
- 1. 什么是原型?
- 2. 原型的作用是什么?
- 3. 原型中this的指向是什么?
- 函数对象
- 1. prototype
- 2. _proto _
- 3. constructor
- 原型链
- 终极图
构造函数
通过
new 函数名
来实例化对象的函数叫构造函数。
任何的函数都可以作为构造函数存在。之所以有构造函数与普通函数之分,主要从功能上进行区别的,构造函数的主要 功能为 初始化对象,特点是和new 一起使用。new就是在创建对象,从无到有,构造函数就是在为初始化的对象添加属性和方法。构造函数定义时首字母大写(规范)。
function Person(name) {this.name = name;
}let p1 = new Person('张三'); // 实例化console.log(p1); // Person {name: "张三"}
此时,p1
就是一个新对象。
1. new一个新对象的过程,发生了什么?
- 创建一个空对象
obj {}
- 空对象的
_proto_
指向了构造函数的prototype成员对象 - 使用
apply
调用构造器函数,属性和方法被添加到 this 引用的对象中 - 如果构造函数中没有返回其它对象,那么返回 this,即创建的这个的新对象,否则,返回构造函数中返回的对象
对new理解:new 申请内存, 创建对象,当调用new时,后台会隐式执行new Object()创建对象。所以,通过new创建的字符串、数字是引用类型,而是非值类型。
2. 手写new函数
function _new(func, ...args) {// 1. 创建空对象let obj = {};// 2. 空对象的_proto_指向了构造函数的prototype成员对象obj.__proto__ = func.prototype; // 一二步合并就相当于 let obj = Object.create(func.prototype)// 3. 使用apply调用构造器函数,属性和方法被添加到 this 引用的对象中let result = func.apply(obj, args);// 4. 确保 new 出来的是个对象return typeof result === 'object' ? result : obj;
}
测试用例:
function Person(name, age) {this.name = name;this.age = age;
}let obj = _new(Person, 'xia', 20);console.log(obj); // Person {name: "xia", age: 20}
3. 构造函数上的方法
- 在构造函数上直接定义方法(不共享)
function Person() {this.say = function () { // 直接定义方法console.log('hello');}
}let p1 = new Person();
let p2 = new Person();
p1.say(); // hello
p2.say(); // helloconsole.log(p1.say === p2.say); // false
很明显,p1 和 p2 指向的不是一个地方。 所以 在构造函数上通过 this 来添加方法的方式来生成实例,每次生成实例,都是新开辟一个内存空间
存方法。这样会导致内存的极大浪费,从而影响性能
。
- 通过原型添加方法(共享)
构造函数通过原型分配的函数,是所有对象共享的。
function Person(name) {this.name = name;
}
Person.prototype.say = function () { // 通过原型添加方法console.log('hello ' + this.name);
}let p1 = new Person('张三');
let p2 = new Person('李四');
p1.say(); // hello 张三
p2.say(); // hello 李四console.log(p1.say === p2.say); // true
所以我们经常 将公共属性定义到构造函数里,将公共方法放到原型对象上
。
点击查看“构造函数的五种继承方式”
原型
1. 什么是原型?
上面的Person.prototype
就是原型(也叫显式原型
),它是一个对象,我们也称它为原型对象。
2. 原型的作用是什么?
原型的作用,就是共享方法。
我们通过 Person.prototype.say
可以共享方法,不会反复开辟存储空间,减少内存浪费。
3. 原型中this的指向是什么?
指向实例化对象p1、p2
函数对象
__proto__
:任何对象( JS 中万物皆对象)都有__proto__属性(隐式原型)prototype
:所有函数(仅限函数)都拥有 prototype 属性(显式原型)constructor
:所有的 prototype 和 实例化对象 都有一个constructor 属性,都指向关联的构造函数本身
当我们声明一个function关键字的方法时,会为这个方法添加一个prototype属性,指向默认的原型对象,并且此prototype的constructor属性也指向方法对象。此二个属性会在创建对象时被对象的属性引用。
function Hello() {}; // 构造函数
var h = new Hello(); // 实例化对象// 所有函数都有个prototype属性(显式原型)
console.log(Hello.prototype); // Object {} 原型对象
// 构造函数的prototype属性有个constructor属性,指向构造函数本身
console.log(Hello.prototype.constructor === Hello); // true
// 实例化对象没有prototype属性、只有函数才有prototype属性
console.log(h.prototype); // undefined// 实例化对象的constructor属性指向构造函数本身
console.log(h.constructor === Hello); // true
// 即
console.log(h.constructor === Hello.prototype.constructor); // true // 所有引用类型都拥有__proto__属性(隐式原型)
console.log(h.__proto__ === Hello.prototype); // true
// 即
console.log(h.__proto__ === h.constructor.prototype); //true
// 即
console.log(Hello.prototype === h.constructor.prototype); //true
// 即
console.log(Hello === h.constructor); // true
1. prototype
所有函数(仅限函数)都拥有 prototype 属性(显式原型)
function Person() {};Person.prototype.sayHello = function() {console.log('Hello!')
}var person1 = new Person();
var person2 = new Person();console.log(person1.sayHello === person2.sayHello) // true,同一个方法
prototype对象用于放某同一类型实例的共享属性和方法,实质上是为了内存着想。
讲到这里,你需要知道的是,所有函数本身是Function函数的实例对象,所以Function函数中同样会有一个prototype对象放它自己实例对象的共享属性和方法。
// 实例化对象的constructor属性 指向构造函数本身
console.log(person1.constructor === Person); // true
console.log(person2.constructor === Person); // true// Person是Function的实例对象
console.log(Person.constructor === Function); // true
console.log(Function.constructor === Function); // true
如下图:
2. _proto _
任何对象(JS中万物皆对象)都有__proto__属性(隐式原型)
function Person() {};Person.prototype.sayHello = function() {console.log('Hello!')
}var person1 = new Person();
var person2 = new Person();// 所有引用类型都有__proto__属性,指向构造函数的显示原型
console.log(person1.__proto__ === Person.prototype); // true
console.log(person2.__proto__ === Person.prototype); // true
/*1、字面量方式*/
var a1 = {};
console.log(a1.constructor === Object); // true (即构造器Object)
console.log(a1.__proto__ === a1.constructor.prototype); // true
console.log(a1.__proto__ === Object.prototype); // true/*2、构造器方式*/
var A = function (){};
var a2 = new A();
console.log(a2.constructor === A); // true(即构造器function A)
console.log(a2.__proto__ === a2.constructor.prototype); // true
console.log(a2.__proto__ === A.prototype); // true/*3、Object.create()方式*/
var a1 = {a:1}
var a2 = Object.create(a1);
console.log(a2.constructor === Object); // true (即构造器Object)
console.log(a2.__proto__ === a1); // true
console.log(a2.__proto__ === a2.constructor.prototype); //false
3. constructor
所有的 prototype 和 实例化对象 都有一个constructor 属性,都指向关联的构造函数本身
function Person() {};
var person1 = new Person();
var person2 = new Person();console.log(person1.constructor === Person); // true
console.log(Person.constructor === Function); // true
console.log(Function.constructor === Function); // true
- person1 与 person2 是 Person 对象的实例,他们的 constructor 指向创建它们的构造函数,即 Person 函数;
- Person 是函数,但同时也是 Function 实例对象,它的 constructor 指向创建它的构造函数,即 Function 函数;
- Function 函数,它是JS的内置对象,它的构造函数是它自身,所以内部 constructor 属性指向自己。
所以constructor属性其实就是一个拿来保存自己构造函数引用的属性,没有其他特殊的地方。
原型链
var A = function () {};
var a = new A();// 由__proto__组成的原型链
console.log(a.__proto__ === A.prototype); // true
console.log(A.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__ === null); // true null表示“没有对象”,即该处不应该有值。
下图中由相互关联的原型组成的链状结构就是原型链,也就是蓝色的这条线
function Person(name, age) {this.name = name;this.age = age;
}
Person.prototype.say = function () {console.log('hello', this.name);
};let student = new Person('张三', 18);console.log(student.__proto__ === Person.prototype); // true
console.log(student.__proto__.say === Person.prototype.say); // true
console.log(student.__proto__.say === student.say); // true
console.log(student.say === Person.prototype.say); // true
对象之所以可以使用构造函数prototype原型对象的属性和方法,就是因为对象有__proto__原型的存在
function Parent(month){this.month = month;
}var child = new Parent('Ann');console.log(child.month); // Ann
console.log(child.father); // undefined
在child
中查找某个属性时,会执行下面步骤:
访问链路为:
终极图
这个图要是看懂了,原型与原型链就基本摸清了。
深入理解javascript原型和原型链相关推荐
- 理解JavaScript中的原型与原型链
理解JavaScript中的原型与原型链 原型链是一种机制,指的是JavaScript中每个内置的对象都有一个内置的__proto__属性指向创建它的构造函数的prototype(原型)属性.原型链的 ...
- JavaScript 开发进阶:理解 JavaScript 作用域和作用域链(上)
作用域是JavaScript最重要的概念之一,想要学好JavaScript就需要理解JavaScript作用域和作用域链的工作原理.今天这篇文章对JavaScript作用域和作用域链作简单的介绍,希望 ...
- JavaScript 开发进阶:理解 JavaScript 作用域和作用域链
作用域是JavaScript最重要的概念之一,想要学好JavaScript就需要理解JavaScript作用域和作用域链的工作原理.今天这篇文章对JavaScript作用域和作用域链作简单的介绍,希望 ...
- [未完整]JavaScript 开发进阶:理解 JavaScript 作用域和作用域链
转载:http://www.cnblogs.com/lhb25/archive/2011/09/06/javascript-scope-chain.html 作用域是JavaScript最重要的概念之 ...
- 理解JavaScript中的原型继承(2)
两年前在我学习JavaScript的时候我就写过两篇关于原型继承的博客: 理解JavaScript中原型继承 JavaScript中的原型继承 这两篇博客讲的都是原型的使用,其中一篇还有我学习时的错误 ...
- 快速理解JavaScript面向对象编程—原型
总的来说js语言就是门面向对象编程的语言,对象这个概念几乎贯穿了整个js的学习. 对象 创建对象两种方法:(若要生成对象实例必须调用构造函数) 1.var obj = {name:"jer& ...
- js实现html模板继承,理解JavaScript中的原型和继承
本文主要讲了原型如何在JavaScript中工作,以及如何通过[Prototype]所有对象共享的隐藏属性链接对象属性和方法:以及如何创建自定义构造函数以及原型继承如何工作以传递属性和方法值. 介绍 ...
- 深入理解Javascript作用域和作用域链
什么是作用域 作用域是代码运行时某些特定的部分中变量.函数和对象的可访问性,换句话说,作用域决定了代码块中变量和其他资源的可见性 作用域共有两种工作模型 词法作用域(静态作用域) 动态作用域 词法作用 ...
- js面试与笔试---理解 JavaScript 作用域和作用域链
任何程序设计语言都有作用域的概念,简单的说,作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期.在JavaScript中,变量的作用域有全局作用域和局部作用域两种. 1. ...
- 几道题目理解JavaScript作用域、作用域链、预解析规则、表达式
先看题目 1.结果是 undefined console.log(a)var a = 1 复制代码 2.报错 Uncaught ReferenceError: Cannot access 'a' be ...
最新文章
- lnmp上搭建zabbix
- node mysql批量写入_如何使用node.js在MySQL中进行批量插入
- 怎么查看linux是不是as7u4,Linux下搭建Android开发环境
- pyinstaller 打包selenium脚本 取消cmd
- 成功解决Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX AVX2
- 【译】10 years Blockchain. The Race is on: Blockchain vs. Tangle vs. Hashgraph
- 【ntp】虚拟机时间莫名异常
- 安卓APP_ 布局(7) —— ViewPager翻页
- 现代软件工程的《构建之法》
- 宝塔LNMP使用步骤nginx+php 7.2
- 继承关系 c# 1613704854
- 网络爬虫生成代理ip
- java进行股票交易_基于Java的股票交易系统设计与开发
- 关于“男孩出剪刀,女孩出布”,你怎么看,你又会怎么选择?
- ORB、SURF、SIFT特征点提取方法和ICP匹配方法
- 部署dashboard
- scip指令集_C++上位机与安捷伦agilent示波器的通讯
- 面试题-专业名称诠释
- 五、K3 WISE 开发插件《直接SQL报表开发新手指导 - BOM成本报表》
- 在linux系统命令行模式下如何输入中文