JavaScript高级 |彻底搞懂原型对象
本文已收录于专栏 ⭐️ 《JavaScript》⭐️
学习指南:
- 对象的原型
- 函数的原型
- new操作符
- 将方法放原型里
- constructor
- 总结梳理
- 原型对象
- 内存表现
- 完结散花
- 参考文献
对象的原型
JavaScript 当中每个对象都有一个特殊的内置属性[[prototype ]] ,这个特殊的对象可以指向另外一个对象。
那这个对象有什么用呢?
- 当我们通过引用对象的属性
Key
来获取一个value时,它会触发[[Get]]的操作; - 这个操作会首先检查该对象是否有对应的属性,如果有点话就使用它。
- 如果对象中没有该属性,那么会访问对象的内置属性[[prototype]],在此属性指向的对象中查找是否有该属性。
- 只要是对象都会都有这样的一个内置属性。
属性 [[Prototype]] 是内部的⽽且是隐藏的,所有我们可获取的方式有两种:
- 方式一:通过对象的_proto_属性可以获取到(早期路由器自己添加的,存在一定的兼容性问题)
- 方式二:通过Object.getPrototypeOf 方法可以获取到。
函数的原型
- 将函数看成是一个普通函数的对象时,它是具备
[[Prototype]]
或者说是_proto_
(隐式原型)。
var obj = {}
function fun(){}console.log(obj._proto_)
console.log(fun._proto_)
- 将函数看成是一个普通函数时,它是具备
prototype
属性的。(显式原型)
var obj = {}
function fun(){}console.log(fun.prototype)
// console.log(obj.prototype) 对象是没有prototype属性的。
所以 只有函数才具有prototype
属性。
注意这里的prototype
和[[prototype]]是完全不同的两个概念。
new操作符
new Person();
new完 之后会发生什么?
var obj = {};
- 创建空对象。
this = obj;
- 将这个空对象赋值给
this
。
obj._proto_ = Person.prototype
- 将函数的显示原型赋值给创建的这个空对象的
_proto_
属性作为它的隐式原型。 - 执行函数体中的代码。
- 将这个对象默认返回。
再举一个例子来理解一下。
function foo(){}
console.log(foo.prototype);// 先打印一下 foo 属性
var fun = new foo();// 创建空的对象// 将foo的prototype原型(显示原型)赋值给空对象的_proto_(隐式原型)
console.log(fun._proto_);console.log(foo.prototype === fun._proto_)// true
将方法放原型里
function Student(name,age,sno){this.name = name;this.age = age;this.sno = sno;this.running = function(){console.log(this.name +" running");}this.eating = function(){console.log(this.name +" eating");}this.studying = function(){console.log(this.name +" studying");}
}var stu1 = new Student("why",18,111);
var stu2 = new Student("kobe",16,112);
var stu3 = new Student("jame",15,113);
之前我们编写多个函数方法的时候,会直接在对象中创建。
但我们发现在每调用一次函数时,都会同时创建多个相同的函数对象。
比如上面的例子中三个函数对象running
、eating
、studying
都分别创建了三次。
那有没有一种方法让我们每种对象只需要创建一次,然后共享这些属性呢?
答案就将函数方法放到显示原型里面!
function Student(name,age,sno){this.name = name;this.age = age;this.sno = sno;
}
Student.prototype.running = function(){console.log(this.name + "running")}
Student.prototype.eating = function(){console.log(this.name +" eating");}
Student.prototype.studying = function(){console.log(this.name +" studying");}
var stu1 = new Student("why",18,111);
var stu2 = new Student("kobe",16,112);
var stu3 = new Student("jame",15,113);
Student.prototype.running = function()
- 由构造函数创建出来的所有对象,都会共享这些属性,而且每种只创建一次。
查找原理
- 先在对象内部进行查找。
- 如果没有找到,就去原型里查找。
那可不可以将其他属性也放到原型里面?
答案是否定的。
因为每个对象的属性值都是不一样的,而原型只有一个。
constructor
事实上原型对象上面是有一个属性的:constructor
- 默认情况下原型上都会添加一个属性叫做
constructor
,这个constructor
指向当前的函数对象。 - Person === Person.prototype.constructor
function Person{}
console.log(Person)//[Function:Person]
console.log(Person.prototype.constructor); //[Function:Person]
console.log(p1.__proto__.constructor);//[Function:Person]
console.log(p1.__proto__.constructor);// Person
function Person(name,age){this.name = name;this.age = age;
}
对应内存图如下:
- 创建出来的对象中的
prototype
属性指向了显示原型对象的constructor
function Person(name,age){this.name = name;this.age = age;
}
var p1 = new Person("why",18);
var p2 = new Person("kobe",30);console.log(p1.name);
console.log(p2.name);
new
出来的两个新的空对象的prototype
属性指向了显示原型对象的constructor
function Person(name,age){this.name = name;this.age = age;
}
//新增方法
Person.prototype.running = function(){console.log("running");
}
var p1 = new Person("why",18);
var p2 = new Person("kobe",30);p1.running();
p2.running();
- 在Person的原型上添加了
running
函数,于是新开辟的running
这块内存也指向了Person显式原型对象
。+
function Person(name,age){this.name = name;this.age = age;
}
Person.prototype.running = function(){console.log("running");
}
var p1 = new Person("why",18);
var p2 = new Person("kobe",30);console.log(p1.name);
console.log(p2.name);p1.running();
p2.running();//新增属性
Person.protptype.address = "中国"
p1.__proto__.info = "中国很美丽!"p1.height = 1.88p2.isAdmin = true;
- 在原型上添加新的属性并赋值,
Person显式原型对象
的内存中同样也声明了新增的这些属性。 - 在
p1对象
中新增了height
属性,在p2对象
中新增了isAdmin
属性。
function Person(name,age){this.name = name;this.age = age;
}
Person.prototype.running = function(){console.log("running");
}
var p1 = new Person("why",18);
var p2 = new Person("kobe",30);console.log(p1.name);
console.log(p2.name);p1.running();
p2.running();//新增属性
Person.protptype.address = "中国"
p1.__proto__.info = "中国很美丽!"p1.height = 1.88p2.isAdmin = true;// 修改address
p1.address = "河北省"
console。log(p2.address)
Person显式原型对象
中的address
属性并未被覆盖,而是被加到了p1对象
里面。p2.address
打印的仍然是Person显式原型对象
中的中国
。
Person.prototype.message = "Hello Person";Person.prototype.info = {name:"沈七",age:30};Person.prototype.running = function(){};Person.prototype.eating = function(){};
我们也可以通过直接赋值一个新的原型对象来简写上面代码。
Person.prototype = {message:"Hello Person",info:{name:"沈七",age:30},running:function(){}eating:function(){}constructor:Person
}
总结梳理
隐式原型
- JavaScript每一个对象都有
[[prototype]]
属性,它的属性值是对某个特定对象的引用。 - 我们把这里的“某个特定对象”称为该实例对象的原型,也称之为隐式原型。
- 但
[[prototype]]
属性是内部且是隐藏的,所以我们需要__proto__
属性来操作[[prototype]]
属性,因为__proto__
属性存在于每一个对象当中且允许被访问。
显式原型
- 对于每一个函数对象(非箭头函数)其都有
prototype
属性,被叫做显式原型。 prototype
也会指向一个对象,这个对象的所有属性和方法都会被构造函数的实例所继承,这对象被成为原型对象。
两者之间的关系
每一个实例对象都通过__proto_
指向它的构造函数的原型对象。
这是因为new
操作符的底层实现决定的。
- 当一个对象通过构造函数
new
出实例后 - 该对象
prototype
显式原型会通过__proto__
赋值给[[prototype]]
的隐式原型。
obj._proto_ = Person.prototype
注意[[prototype]]
和 prototype
是完全不同的两个属性.
原型对象
原型对象里面都有两个属性:__proto__
和constructor
constructor
:用来记录实例对象是由哪个构造函数创建的,所以它默认指向创建它的构造函数。__proto__
:原型对象也是对象,所以它也有__proto__
属性。
内存表现
function Person(){}
var p1 = new Person();
Person
的prototype
属性会指向它的显式原型,即Person函数的原型对象
。Person函数的原型对象
的constructor
属性会指向创建它的构造函数,即Person
。new
出来的实例对象p1
的隐式原型__proto__
会自动指向创建它的构造函数的显式原型,即Person函数的原型对象
。
console.log(p1._proto == Person.prototype)//true
- 因为
Person函数的原型对象
本身也是一个对象,所以是由Object``new
出来的。 - 所以
Person函数的原型对象
的隐式原型__proto__
属性指向Obejct的原型对象
的显式原型。
console.log(Person.prototype.__proto__ == Obejct.prototype)//true
Object
作为顶级父类,它的原型对象的隐式原型__proto__
属性指向null
。
console.log(Object.prototype.__proto == null)// true
那
Person
对象是由谁创建出来的呢?,它的隐式原型又指向谁?
我们来打印一下试试。
function Person(){}
console.log(Person.__proto__)
我们发现Person.__proto__
实际上是指向Function.prototety
的。
也就是说:
Function
是所有直接声明的函数的构造函数。- 所有直接声明的函数都是
Function
的一个实例对象。 - 所有直接声明的函数的
__proto__
都指向同一个地方那就是Function.prototety
.
我们可以进一步验证:
function Person(){}
function foo(){}
console.log(Person.__proto__==Function.prototype);// true
console.log(foo.__proto__==Function.prototype); // true
console.log(foo.__proto__==Person.prototype); // true
因为Function函数的原型对象
本身也是一个对象,所以Function函数的原型对象
的隐式原型__proto__
也指向Obejct
的显式原型.
console.log(Function.prototype.__proto__ == Object.prototype);// true
Obejct
本身也是一个函数,所以它的隐式原型__proto__
指向Function函数的原型对象
的显式原型。
console.log(Object.__proto__ == Function.prototype);// true
Function
的本身也是一个函数,所以它的隐式原型__proto__
指向它自己的Function函数的原型对象的显式原型。
console.log(Function.__proto__ == Function.prototype);// true
于是就有了下面这张图。
小结:
p1
是Person
的实例对象。obj
是Object
的实例对象。Function
/Object
/FOO
都是Function
的实例对象。- 原型对象默认创建时,隐式原型都是指向
Object
的显式原型的。(Object
指向null)
又因为:
当A类的原型对象的隐式原型__proto__
指向B类的显式对象时,我们称之为A类是继承于B类的。
if(A.prototype.__proto__ == B.prototype){A 继承于 B
}
推到出的结论:
Object
是Person
/Function
的父类
Function
与Object
的关系:
Object
是Function
的父类。Function
是Object
的构造函数。
完结散花
ok以上就是对 JavaScript高级 |彻底搞懂原型对象 的全部讲解啦,很感谢你能看到这儿。如果有遗漏、错误或者有更加通俗易懂的讲解,欢迎小伙伴私信我,我后期再补充完善。
参考文献
coderwhy老师JS高级视频教程
JavaScript高级 |彻底搞懂原型对象相关推荐
- java 原型图_一张图搞懂原型、原型对象、原型链
基本概念 在javascript中,函数可以有属性. 每个函数都有一个特殊的属性叫作原型(prototype) 每个对象拥有一个原型对象 [[Prototype]] / __proto__ / Obj ...
- 猿创征文|在工作中彻底搞懂原型和原型链的原理
前言 在前端开发过程中,涉及到JS原理相关的内容也就是常用的几大模块,不仅常用而且很重要,但是涉及到原理的话会有点难懂,尤其是对JS接触不太久的开发者来讲,甚至工作好几年的开发者也只是在平时开发中知道 ...
- JavaScript高级之构造函数和原型
1.1 概述 在典型的 OOP 的语言中(如 Java),都存在类的概念,类就是对象的模板,对象就是类的实例,但在 ES6之前, JS 中并没用引入类的概念. ES6, 全称 ECMAScript 6 ...
- JavaScript——面向对象之继承(原型对象)与多态(重载、重写)
继承与多态 引入问题 一.继承 1. 步骤 (1) 找到所有子对象共同的父对象 (2) 将所有子对象公共的方法定义添加到共同的父对象中 (3) 所有子对象因继承关系而直接使用父对象中公共的方法 2. ...
- 弄懂原型对象和原型链
这些都是自己在学习的过程中整理出来的笔记,希望能帮到大家.因为自己也是个前端菜鸟,只是最前端有很浓厚的兴趣,想往这个方面发展,如果有整理不对的地方,请大家斧正. 首先我们需要知道原型是什么? Java ...
- 一篇文章让你搞懂原型和原型链
每一个构造函数在被创建的时候,会自动创建一个相应的对象,这个对象就是原型对象,这个函数有一个指向该对象的指针.举个例子: 下面创建了一个函数person. function person () { } ...
- Javascript深入理解构造函数和原型对象
1.在典型的oop的语言中,如java,都存在类的概念,类就是对象的模板,对象就是类的实例.但在js中不存在类的概念,js不是基于类,而是通过构造函数(constructor)和原型链(propoty ...
- JavaScript 进阶 35 -- 构造函数、原型对象、实例之间的关系详解
说到继承 ,先要弄明白 :构造函数.原型对象.实例之间的关系. 先来了解几个简单的概念: 构造函数:通过 关键字 new 来调用的 函数,var p = new Person():Person就称为构 ...
- 设计模式系列:搞懂原型模式,你也会分身
原型(Prototype)模式的定义:用一个已经存在的对象实例作为原型,通过复制该原型对象来创建一个和原型对象相同或相似的新对象.属于建造型模式. 原型模式的结构:原型模式主要包含3种角色. 抽象原型 ...
最新文章
- How to use nheqminer in RedHat based systems (CentOS/Fedora)
- some requirement checks failed
- asp.net 的page 基类页面 做一些判断 可以定义一个基类页面 继承Page类 然后重写OnPreLoad事件...
- 听说,他用报表关联数据库表,运维效率提升70%?
- 直播实录丨十年主导15个产品从0到1,她的经验与思考现场拆解
- 计算机网络怎样连手机软件,玩够了手机投屏?了解一下手机如何直接连智能电视...
- arcgis 批量计算几何_GIS中的计算几何
- 利用js完成根据excel填充网页表单
- tp5 微信分享朋友或朋友圈
- 学校运动会开幕式邀请函
- 解决k8s中的长连接负载均衡问题
- 自定义注解校验List集合数据
- 新手如何学习java
- 获取网络图片或本地图片的长宽的方法
- 几计算机网络特,湛江理工职业学校1级MS0ffice了解计算机网络的基本概念和因特网...
- (十八)师大放假了 - 7
- h5 or web缅甸语乱码问题
- 寄存器某一位置位或者清零
- php易信短信接口,易信公众平台demo代码php(含验证接口)
- 图片采集器-网页图片批量采集器免费