本博客介绍了以下知识点:
1、Global Object (全局对象或Global对象);
2、ECMAScript规定的函数(函数也属于对象);
3、公用属性(原型);
4、prototype和__proto__
5、部分总结
看本文之前建议先看看内存图和数据结构的知识,这样理解起来就比较容易。
bomber:算法和数据结构zhuanlan.zhihu.com1、Global Object (全局对象或global对象)
在ECMAScript里面没有说全局对象一定是window,但是浏览器是早于标准(ESCMscript)的,浏览器默认的全局对象就是window。
全局对象的中文名字是英文翻译过来的,所以它实际在ECMAScript标准中是叫做了Global Object,中文翻译过来叫做全局对象,这个对象在某个具体的接口或者环境中就有具体的名字,比如在浏览器中,这个Global Object的具体话就叫做window(如果是其他的环境中,比如是Node.js或者其他,这里的Global Object就不一定是window),window对应的就是浏览器,然后浏览器下面有很多网页,网页对应的是document。
在浏览器中直接输入window或者document都可以找到相应的信息,但是输入global是会报错。
Document 对象使我们可以从脚本中对 HTML 页面中的所有元素进行访问。
Document 对象是 Window 对象的一部分,可通过 window.document 属性对其进行访问。
![4de4a31aaafdb9c5037849844fce7ddc.png](https://i-blog.csdnimg.cn/blog_migrate/6fd3ee302821ed2119800381c93affea.jpeg)
window点击三角形后会显示所有的属性和方法(函数或对象),太多了无法全部展示。
![cf9139fbde4534adc863405536a04012.png](https://i-blog.csdnimg.cn/blog_migrate/2ef60f1ccc932b7c5501f0dbdac301d2.jpeg)
document点击三角形后会显示脚本中对 HTML 页面中的所有元素
![0e7e5d1d6e68d1a0b14ccfb098c45735.png](https://i-blog.csdnimg.cn/blog_migrate/7666d1949e07ee4105928f8107ec5365.jpeg)
详细可以参考MDN或W3C文档
Window 对象www.w3school.com.cn HTML DOM Document 对象www.w3school.com.cn JavaScript 全局对象www.w3school.com.cn JavaScript 标准库developer.mozilla.org![b1732b110a40f05dc19225039a572d9f.png](https://i-blog.csdnimg.cn/blog_migrate/c3902270a6f64f97293402c41721213d.jpeg)
![b1732b110a40f05dc19225039a572d9f.png](https://i-blog.csdnimg.cn/blog_migrate/c3902270a6f64f97293402c41721213d.jpeg)
![b1732b110a40f05dc19225039a572d9f.png](https://i-blog.csdnimg.cn/blog_migrate/c3902270a6f64f97293402c41721213d.jpeg)
![76837273775e13e32fba1ed518cebe7b.png](https://i-blog.csdnimg.cn/blog_migrate/901217b27e8fe8153fc54c991c6035a8.jpeg)
在特定浏览器的情况下:表格中的parseInt()、parseFloat()等,实际都是全局对象window的方法;还有就是常见的一些特殊值,如:null、undefined等都是它的属性,以及一些构造函数Object、Array等也都是它的方法,具体属性、方法和对象可以看Window的MDN或者W3C链接。
(1)window可以省略,因为window是全局对象,所以默认所有属性和对象方法对象都可以访问。
![1923e544a9d8a5318a8c7a67b9c38b43.png](https://i-blog.csdnimg.cn/blog_migrate/0685cb045553f9644827de7947cbd0f7.png)
(2)特定浏览器中的属性效果会有所不同。以alert为例
在chrome浏览器中显示
![a39377dd8ae5fabbefe7fb0ec2d3a058.png](https://i-blog.csdnimg.cn/blog_migrate/51992c7faf9604c333ca80d07a660f1c.jpeg)
在IE11浏览器中显示
![5300196854fa3fb9f509fcde1ca3e74b.png](https://i-blog.csdnimg.cn/blog_migrate/1b3010c0e5d7dae59c749cfa9b8d3a4b.jpeg)
效果不太一样。
(3)ECMA机构制定ECMAScript标准规定的举例——Window.setTimeout()
Window.setTimeout()可以设置一个参数
var
效果如下
![cac5c16f6618150b2571084e27d8bdd0.png](https://i-blog.csdnimg.cn/blog_migrate/12f87b2ee75ad3a9fb00ff7d10ba08d6.jpeg)
可以看到每过3秒左右就会显示hi,并且后面还返回了一个数字(timeouID),这个数字表示第几个计时器,类似一个编号,用于标识调用创建的计时器setTimeout(),可以传递此值clearTimeout()以取消超时,只要不关闭或者刷新页面,这个数字(timeouID)会一直增加,详见链接说明
WindowOrWorkerGlobalScope.setTimeout()developer.mozilla.org![b1732b110a40f05dc19225039a572d9f.png](https://i-blog.csdnimg.cn/blog_migrate/c3902270a6f64f97293402c41721213d.jpeg)
而该方法不仅在浏览器中可以实现,在node.js中也是可以实现的,具体看链接
Node.js setTimeout和setIntervalwww.w3cschool.cn(4)ECMA机构制定ECMAScript标准规定的举例二——window.history
Window.historydeveloper.mozilla.org![b1732b110a40f05dc19225039a572d9f.png](https://i-blog.csdnimg.cn/blog_migrate/c3902270a6f64f97293402c41721213d.jpeg)
Javascript里面的对象history是有自己的BOM(浏览器对象模型)规范,这个规范不是一个严格的标准。
Window.history是一个只读属性,用来获取History对象的引用,History对象提供了操作浏览器会话历史(浏览器地址栏中访问的页面,以及当前页面中通过框架加载的页面)的接口。
history
效果如下:
![361a4d9cb12b292a2678d764f86e1c62.png](https://i-blog.csdnimg.cn/blog_migrate/1ba41d98109d043b49b1dc7e699e1867.jpeg)
除了以上两种:
1. 一种是ECMAScript规定的属性或对象(这里的global如果是在浏览器中使用就是window);
2. 一种是特定浏览器自己加的属性或对象(例如chrome或者Firefox等一种是标准规定的和浏览器自身以外。
还可以自己创建属性值和方法。例如:
如果直接输入
window.EnglishName
window.age
会返回undefined
直接输入
window.personName()
window.personAge()
会报错
![90bf04213dd3b085f8245966b63e983b.png](https://i-blog.csdnimg.cn/blog_migrate/cce14522a0f24bd709337c7e5d22bdd9.jpeg)
但是经过声明之后,window就可以访问到它们。
![190677dd99d9379da1f32d49864f3e53.png](https://i-blog.csdnimg.cn/blog_migrate/9e6c5f16738954cea5630dfa41c170da.jpeg)
这里定义了两个名为EnglishName 和age的全局变量和两个名为personName()和personAge()的全局函数。之后再使用window就可以取到相应的属性值和函数结果,说明personName()和personAge()的全局函数是window对象的方法,EnglishName 和age的全局变量是window对象的属性。
2、ECMAScript规定的函数(函数也属于对象)
前面应学过了数据的几种类型,那么除了对象(Object)以外其他的还有6种类型
bomber:《JS 历史、数据类型、对象、typeof》zhuanlan.zhihu.com其中符号(symol)暂时不考虑,并排除null和undefined,那么剩下的3种类型可以作为对象吗?
另外
Number()前面说明过把其他类型转换为数字;
String()前面说明过可以把其他类型转换为字符串;
Boolean()前面说明过可以把其他类型转换为布尔;
bomber:《JS类型转换、内存图、GC、深/浅拷贝》zhuanlan.zhihu.com这三个方法除了能够转换类型以外还有别的作用吗?
(2-1)Number(数字)作为对象
var
通过显示可以看到a只有数字1,但是number是大括号包围起来。
![d7cffc3cc2693c98db4ba8e5762ea4e6.png](https://i-blog.csdnimg.cn/blog_migrate/ccc3ac2be2742c6c677e267dfbb33f2e.jpeg)
点开Number{1}之后发现有一些函数可以继续操作,挑选几个测试如下:
![26b5449bb980358d3980d36467a5f11a.png](https://i-blog.csdnimg.cn/blog_migrate/994c73ae16d01c54cf7eaf2b95e9a7b7.jpeg)
toExponential()
Number.prototype.toExponential()developer.mozilla.org![b1732b110a40f05dc19225039a572d9f.png](https://i-blog.csdnimg.cn/blog_migrate/c3902270a6f64f97293402c41721213d.jpeg)
toFixed()
Number.prototype.toFixed()developer.mozilla.org![b1732b110a40f05dc19225039a572d9f.png](https://i-blog.csdnimg.cn/blog_migrate/c3902270a6f64f97293402c41721213d.jpeg)
toString()
Number.prototype.toString()developer.mozilla.org![b1732b110a40f05dc19225039a572d9f.png](https://i-blog.csdnimg.cn/blog_migrate/c3902270a6f64f97293402c41721213d.jpeg)
valueOf()
Object.prototype.valueOf()developer.mozilla.org![b1732b110a40f05dc19225039a572d9f.png](https://i-blog.csdnimg.cn/blog_migrate/c3902270a6f64f97293402c41721213d.jpeg)
Primitive
Primitivedeveloper.mozilla.org![b1732b110a40f05dc19225039a572d9f.png](https://i-blog.csdnimg.cn/blog_migrate/c3902270a6f64f97293402c41721213d.jpeg)
但是a作为一个数字1居然也可以使用点.操作符。
![7726735b44cfeb8a869b115f7d519769.png](https://i-blog.csdnimg.cn/blog_migrate/273cfa4390e82eb14c456aae85c3254d.jpeg)
这个问题某些方面可以说是JS的历史原因
详细历史看前面的博客
bomber:《JS 历史、数据类型、对象、typeof》zhuanlan.zhihu.comJS之父的老板想要JavaScript跟Java一样,所以JS之父发明了两种,一种是为了满足他老板的需求(var number=newNumber(1)),这种方法很少人用。
另一种是他认为正常的使用方法(var a=1),而这种方法被JS程序员所喜欢,但是该方法存在一个缺点。因为非对象是不可以使用属性访问器的点操作符。而这里的数字1却可以用属性访问器,因为JS之父发明了一个妙计,具体步骤如下:
- 当你在使用属性访问器的时候会声明一个临时对象(例如temp对象);
var
- 把temp.toString()的值作为n.toString()的值;
n
- 删除掉temp.toString()
内存图
![4134f2b161b2d6efc0a531b48c672e5d.png](https://i-blog.csdnimg.cn/blog_migrate/b40a8a4f33eff0bb2a32cbf4839894be.jpeg)
这里如果不通过声明和赋值var a=1,而直接使用1来操作会报错,但是加上括号,或者1后面两个点就可以继续操作。
![bae89d918b8a3dd94431e798964a6501.png](https://i-blog.csdnimg.cn/blog_migrate/9bc9007cc378722c60706e9067b8309a.jpeg)
两点..可以的原因:
因为前面的1.会把它当做小数,然后突然接一个t,浏览器就看不懂了。
如果是1.然后后面再增加一个.toString(),那这里就会有一个点.操作符,就不会报错了。
括号()可以的原因:
括号英文名叫Grouping operator,翻译成中文叫做圆括号运算符,也可以叫做分组运算符,因为是翻译过来的,所以翻译成你觉得开心的词语都行。
可以的原因就是先对括号里面的的值进行运算,(1)也就是1,没啥好计算了。
![b1732b110a40f05dc19225039a572d9f.png](https://i-blog.csdnimg.cn/blog_migrate/c3902270a6f64f97293402c41721213d.jpeg)
所以这里的a还可以使用属性访问器,比如点操作符来增加key-value,但是增加的key-value是存到临时对象temp里面,所以可以增加key-value,但是无法通过属性访问器使用这个key再次获取到对应的value。
![0d33d5468596d9814001da956b3ac1c9.png](https://i-blog.csdnimg.cn/blog_migrate/7b0dbb9eab7f8ae6aedb40ae6d74ed22.jpeg)
内存图:
![5b0856119ba44e93e493ccd93a8964d5.png](https://i-blog.csdnimg.cn/blog_migrate/f922e769f9a6cde5b7c017600919d574.jpeg)
(2-2)String(字符串)作为对象
var
通过显示可以看到a只有字符串'abc',但是string是大括号包围起来。
![d8f0a2cbe37b992f3ec780bad7e46d23.png](https://i-blog.csdnimg.cn/blog_migrate/e55d3daa96b4b35985bc2606b3f2bc03.jpeg)
点开String{'abc'}之后会展示与(2-1)中Number{1}不同的地方,String{'abc'}展开后类似一个哈希表,第0个key对应的是'a',第1个key对应的是'b',第2个key对应的是'c',并且还有key是length,对应的是3
它的原始值(primitive Value)是'abc'
![afccedd5945e585c79954e5c47dcac4d.png](https://i-blog.csdnimg.cn/blog_migrate/de2a52a6b6cd5875361340162998612d.jpeg)
可以用属性访问器来访问key来获取到响应的value
![931ad40ab3f5d5b6f404077a4f5a966d.png](https://i-blog.csdnimg.cn/blog_migrate/e1a77b7f139985d83b1a2da83774c51b.jpeg)
点开String{'abc'}之后发现有一些函数可以继续操作,挑选几个测试如下:
![f1d9c7711e75caf7c352009975f04c87.png](https://i-blog.csdnimg.cn/blog_migrate/88845cbf677d300c290379cf6eed1415.jpeg)
这几个是比较老的方法了,标准已经删除了,但是某些浏览器还可以支持。
anchor()
String.prototype.anchor()developer.mozilla.org![b1732b110a40f05dc19225039a572d9f.png](https://i-blog.csdnimg.cn/blog_migrate/c3902270a6f64f97293402c41721213d.jpeg)
big()
String.prototype.big()developer.mozilla.org![b1732b110a40f05dc19225039a572d9f.png](https://i-blog.csdnimg.cn/blog_migrate/c3902270a6f64f97293402c41721213d.jpeg)
blink()
String.prototype.blink()developer.mozilla.org![b1732b110a40f05dc19225039a572d9f.png](https://i-blog.csdnimg.cn/blog_migrate/c3902270a6f64f97293402c41721213d.jpeg)
bold()
String.prototype.bold()developer.mozilla.org![b1732b110a40f05dc19225039a572d9f.png](https://i-blog.csdnimg.cn/blog_migrate/c3902270a6f64f97293402c41721213d.jpeg)
继续挑选几个:
![192daebb0a5066f368e6383a8ad0949a.png](https://i-blog.csdnimg.cn/blog_migrate/63ab516f4fed03ad5ca7c0f2a25144bc.jpeg)
charCodeAt()
String.prototype.charCodeAt()developer.mozilla.org![b1732b110a40f05dc19225039a572d9f.png](https://i-blog.csdnimg.cn/blog_migrate/c3902270a6f64f97293402c41721213d.jpeg)
charAt()
String.prototype.charAt()developer.mozilla.org![b1732b110a40f05dc19225039a572d9f.png](https://i-blog.csdnimg.cn/blog_migrate/c3902270a6f64f97293402c41721213d.jpeg)
再来几个:
![c7b9fe51acc6d5f5ecff92ed55b996d1.png](https://i-blog.csdnimg.cn/blog_migrate/85c64dc14682bb0c7b5c503fb05903b3.jpeg)
![3c68834f505ac50e8fdf6b2599a1d0c3.png](https://i-blog.csdnimg.cn/blog_migrate/9ee9b7fac853e476c8e98f128054f761.jpeg)
这里的concat 是跟命令行有关,命令行用的是cat
![0e1e6e9feb920ef0f324ecbda8030eaf.png](https://i-blog.csdnimg.cn/blog_migrate/a94ec0d5862e8f64315201808959820d.jpeg)
![3cbfb7b8064138ec250407f808546572.png](https://i-blog.csdnimg.cn/blog_migrate/b08df2b7020daa3b8a84c6cf74d26569.jpeg)
![6dbce503564ffe54f4db69a7b1188a2f.png](https://i-blog.csdnimg.cn/blog_migrate/b20b6656fae3eee02965f99e5a54f845.jpeg)
trim()
String.prototype.trim()developer.mozilla.org![b1732b110a40f05dc19225039a572d9f.png](https://i-blog.csdnimg.cn/blog_migrate/c3902270a6f64f97293402c41721213d.jpeg)
concat()
String.prototype.concat()developer.mozilla.org![b1732b110a40f05dc19225039a572d9f.png](https://i-blog.csdnimg.cn/blog_migrate/c3902270a6f64f97293402c41721213d.jpeg)
slice()
String.prototype.slice()developer.mozilla.org![b1732b110a40f05dc19225039a572d9f.png](https://i-blog.csdnimg.cn/blog_migrate/c3902270a6f64f97293402c41721213d.jpeg)
replace()
String.prototype.replace()developer.mozilla.org![b1732b110a40f05dc19225039a572d9f.png](https://i-blog.csdnimg.cn/blog_migrate/c3902270a6f64f97293402c41721213d.jpeg)
还可以看下面链接,有更多字符串有用的方法:
Useful string methodsdeveloper.mozilla.org![b1732b110a40f05dc19225039a572d9f.png](https://i-blog.csdnimg.cn/blog_migrate/c3902270a6f64f97293402c41721213d.jpeg)
同样的a也可以当做一个对象使用,但是跟(2-1)var a=1一样会借用一个临时对象temp,并且使用完这个临时对象temp后,这个临时对象temp会被抹杀掉。
![ec84432f964669f36cd5fd4fb609475a.png](https://i-blog.csdnimg.cn/blog_migrate/5bbe2a1e3e4522f36309ad690793c895.jpeg)
(2-3)Boolean(布尔)作为对象
var
通过显示可以看到a只有布尔false,但是boolean是大括号包围起来。
![3c47225b1c75874fe044b8c50b571e1d.png](https://i-blog.csdnimg.cn/blog_migrate/7bd9c09cb954c8a240795cd9609966a2.jpeg)
点开Boolean{false}之后会展示与(2-1)一样什么都没有,继续点开__proto__会展示很少几个的函数。
![c1a43634516cf001857cddb65b25887d.png](https://i-blog.csdnimg.cn/blog_migrate/7cd796001deea415876a083a197e0cc2.jpeg)
同样的a也可以当做一个对象使用,但是跟(2-1)var a=1一样会借用一个临时对象temp,并且使用完这个临时对象temp后,这个临时对象temp会被抹杀掉。
![f0561abaf9bb0ec1401728f03f5e940f.png](https://i-blog.csdnimg.cn/blog_migrate/7b66796691038d370b9351d3537660ca.png)
这个注意一个问题,boolean因为已经被当做为一个对象,前面学习过5个falsy值中没有对象,所以他的转换为布尔的值是true
![388d2accccc9597ea090e7c89a8779e2.png](https://i-blog.csdnimg.cn/blog_migrate/3792381daf67f49b4df00f0a53c9b46f.jpeg)
通过前面学习的其他类型转换为布尔的来测试一下
bomber:《JS类型转换、内存图、GC、深/浅拷贝》zhuanlan.zhihu.com使用!!测试
![8eb154edf030ed01e46e23ce49119520.png](https://i-blog.csdnimg.cn/blog_migrate/4ee58442712dd6c9319fb6e1341c59f7.jpeg)
(2-4)对象本身使用new
var
还有一种声明对象的方法,见阮一峰的博客
Object 对象wangdoc.com这两种方式都可以声明对象,唯一的区别是栈内存的地址不同,其他都一样。因为复杂类型对象不是前面的三种基本类型:布尔、数字、字符串。它本身就是对象。
第一种方法(var a={})简单多了,还节省很多字节。
基本上不会用第二种(var object=new Object())方法声明对象,第二种是给以前的Java程序员使用的,为了配合JS之父的老板的某种像Java的设计理念。不过现在Java也可以使用基本类型,不用复杂类型了,所以这种方式算是过时了。
![cbf877afba449add971aa923bb841416.png](https://i-blog.csdnimg.cn/blog_migrate/55b9b39378ad8550ca48ca0e46b17199.jpeg)
画出内存就知道了,对应的地址不
![c0ad7f32abc0a95d0802adb86ab746a7.png](https://i-blog.csdnimg.cn/blog_migrate/02f4230ea85aa4318955bee64e61efcc.jpeg)
所以,只要是新声明的对象,不管内容是否相同,地址都不会相同。当然可以通过(赋值)=来实现相同。
3、公用属性(原型)
经过测试,可以看到,Number,String,Boolean,Object都有toString和valueof属性(key),并且直接通过属性(key)对应的值(value)toString()和valueof()调用前面输入的各类型参数。
var
定义上面的参数后,下面的图可以看到他们都有toString()和valueOf()可以使用。
![2b0b291e52656fbde0cf4febb89b8fc1.png](https://i-blog.csdnimg.cn/blog_migrate/9d95348ee4f2ae9e344089b02a938723.jpeg)
那么内存中是如何存储的呢?画出内存图帮助理解
![ba66058d894205dcfdebb27a5ebf5a9a.png](https://i-blog.csdnimg.cn/blog_migrate/2c4c6d6bd80ac22d94c963097512a2ab.jpeg)
所以可以看到使用公用属性(原型)可以节省很多内存。
那么在JavaScript中是怎么实现的呢?
![599a12b35d0b412443aa2dfcbb33424d.png](https://i-blog.csdnimg.cn/blog_migrate/80ad1863b8e06ae7d9ee9976f6a8c7df.jpeg)
所以在JavaScript中内存图应该是这样
![1621c248f117a0e9c82fdc50bcca0bf9.png](https://i-blog.csdnimg.cn/blog_migrate/31913607a4a6d1d66c0ec2aac15a7e18.jpeg)
经过上面的分析,已经对公用属性(原型)有了初步了解,也知道公用属性(原型)是放在原型对象中。
传入参数的分析情况
普通对象(Object)与数字对象(Number)、字符串对象(String)、布尔对象(Boolean)传入对应参数(数字、字符串、布尔)有什么不同。
var
可以看到用普通对象Object构造与其他对象构造除了本身不相同外,因为本身的地址不同,原型链上的其他节点原型结果完全相同。
复杂类型的普通对象(Object)的括号内参数如果输入了简单类型(Number、String、Boolean),那么会把整个普通对象(Object)转换为相应简单类型的包装对象。
![0c9c8a0fba06ca87958051ca4c67fa79.png](https://i-blog.csdnimg.cn/blog_migrate/0cbd99d20ef72655afa1eaea46355e76.jpeg)
普通对象(Object)与数字对象(Number)、字符串对象(String)、布尔对象(Boolean)不传入参数有什么不同。
var
可以看到用普通对象Object构造与其他对象构造的结果完全不相同;
但是最终的原型都是普通对象Object,并且普通对象Object的原型为null。
Number构造函数创建的对象相当于传入一个数字为0的参数;
String构造函数创建的对象相当于传入一个字符串为空''的参数;
Boolean构造函数创建的对象相当于传入一个布尔为false的参数。
![89ed917f53afeefb5bb415254c22dfd3.png](https://i-blog.csdnimg.cn/blog_migrate/2117ba42624e87deabf3e6eb664adf6a.jpeg)
数字对象(Number)、字符串对象(String)、布尔对象(Boolean)传入不对应参数(数字、字符串、布尔、对象)有什么不同。
var a1=new Number(true)
var a2=new String(10)
var a3=new Boolean('abc')
var a4=new Number({name:'bomber'})
var a5=new String({name:'bomber'})
var a6=new Boolean({name:'bomber'})
可以看到,简单类型包装的对象,如果是传入不对应的参数,那么首先会把括号内的参数转换成相应的包装函数对应的类型,包装函数还是不变。
![ffc1878cbc51bc9e857292d4682bbb6c.png](https://i-blog.csdnimg.cn/blog_migrate/f760d894495f3944f00bd6d2b305304c.jpeg)
那么原型链是什么?
(3-1)普通对象Object的原型链
我们声明两个内容相同的普通对象
var
第2节中我们知道,不管对象的内容是否相同,只要是新声明的,并且没有赋值,那么这两个对象的地址是不相同的,那么是不相等。
但是他们对应的toString()是相等的,都是"[object Object]",可以查看前面对象转换为字符串的博客
bomber:《JS类型转换、内存图、GC、深/浅拷贝》zhuanlan.zhihu.com说明经过函数toString()后返回的值的内容不仅相同,所存的地址也相同,这就是用到了公用属性(原型)——__proto__
![a398ef06d155431cab69e5aa7410e04a.png](https://i-blog.csdnimg.cn/blog_migrate/66963553ef8fd4544f40755532d97335.jpeg)
经过测试,说明这个__proto__可以省略掉,他们是相等的
![24bb12265bdc57634b85d56934423326.png](https://i-blog.csdnimg.cn/blog_migrate/e85063f1ab25d7ab03e225da6ee84346.jpeg)
画出内存图
![598fffe28bfc90e59452d79cfc7d9bcf.png](https://i-blog.csdnimg.cn/blog_migrate/9a63af61d660cd1b680af58c5925042d.jpeg)
普通对象o1本身的真实值valueOf()是本身赋值的对象{name:'bomber',age:18}
o1.__proto__的真实值valueOf()是普通对象Object的公用属性的对象(原型对象)
o1.__proto_.__proto__的结果是null,也就是普通对象Object的公用属性的对象(原型对象)的公用属性的对象(原型对象)是null
![3d84ec562171d4106a189fdf60a56e76.png](https://i-blog.csdnimg.cn/blog_migrate/aeac3dfb41e9087792d71f110251db51.jpeg)
上图的黄色这条线就代表o1对象的原型链,意思就是从对象开头把原型(公用属性)链接起来。
普通对象Object的toString(),括号里面是写入参数是没有效果的,因为普通对象Object的toString()是不接收参数的。
var
![67010b152178268b5abc9ca7333c821d.png](https://i-blog.csdnimg.cn/blog_migrate/b3eda38fe3d9879811da719ed6d9ac6d.jpeg)
Object的MDN链接
Objectdeveloper.mozilla.org![b1732b110a40f05dc19225039a572d9f.png](https://i-blog.csdnimg.cn/blog_migrate/c3902270a6f64f97293402c41721213d.jpeg)
(3-2)new Number(数字对象)、new String(字符串对象)、new Boolean(布尔对象)的原型链
以数字对象举例:
声明三个数字对象
var
并且使用了new Number(数字对象)后,对应的[[PrimitiveValue]]在Number的公有属性(原型)里面会随着括号内的参数变化。
![7628985cc45f9d0acf8128acedeefbe2.png](https://i-blog.csdnimg.cn/blog_migrate/9423bb665e98e73e63936d9b5976a5f0.jpeg)
但是在普通对象Object的公有属性(原型)里面始终为0
如果调用了__proto__,也就是(Number)数字对象的公用属性(原型)后,不管有没有传入参数,真实值始终为0,也就是变为括号里面没有给参数的情况一样。
![d723cccbbd97282c5a2c1ea15ab70c29.png](https://i-blog.csdnimg.cn/blog_migrate/41f7654c81421d5842239253c0855219.jpeg)
对比n1(参数为10)和n2(参数为0)
可以看到除了n1.toString()不等于n2.toString(),其他都是相等的,这是因为n1本身的真实值是10,而n1本身的真实值是0。
![a70c5b180ebfe2b9d8d9331a42662e59.png](https://i-blog.csdnimg.cn/blog_migrate/48dc936a9f0d58ff7b30dc995d5d86a7.jpeg)
对比n2(参数为0)和n3(不写参数)
可以看到n2和n3完全相等,这是因为n2本身的真实值是0,而n3不写参数,默认会认为n3的真实值是0。
![f3dcd4eddbea117c94ca204bc7916a58.png](https://i-blog.csdnimg.cn/blog_migrate/8d60521658f30ebbddf98d9032861af4.jpeg)
Number的toString()括号里面是可以接收参数,代表进制的意思
var
如果括号内不写,代表默认是十进制。如果改成16,就是十六进制,返回的值会按照十六进制自动换算。
![7ced66150003ed0b4f7d7fd591f21972.png](https://i-blog.csdnimg.cn/blog_migrate/bff6f829d95b16098bde3555efe9d8b4.jpeg)
同理可以测试String和Boolean,这里就不一一测试了。
数字(Number)包装对象有自己单独的公用属性(原型)对应的value,这种公用属性(原型)对应的value是普通对象Object没有的,比如第二节里面了解到的toFixed()、toExponential()、toString()(括号里面使用进制)等等;
字符串(String)包装对象有自己单独的公用属性(原型)对应的value,这种公用属性(原型)对应的value是普通对象Object没有的,比如第二节里面了解到的trim()、replace()等等;
布尔(Boolean)包装对象自己的单独的公用属性(原型)对应的value已经被普通对象Object包括了,虽然是与Object一样,但是布尔(Boolean)自己的单独的公用属性(原型)对应的value会覆盖掉Object的公用属性(原型)对应的value;
普通对象有自己单独的公用属性(原型)对应的value,比如hasOwnProperty()等等。
还要包装成对象的注意数字(Number)、字符串(String)、布尔(Boolean)和对象(Object)有自己的各自真实值[PrimitiveValue]],包装成对象的数字(Number)、字符串(String)、布尔(Boolean)和对象(Object)的真实值不一定相等。
![aed89e813d59763152f8f01ebe1e833c.png](https://i-blog.csdnimg.cn/blog_migrate/1da369ef2605a95c880e10cd6ef53d99.jpeg)
以上的箭头除了黑色箭头以外,其他颜色的箭头代表了把对应属性的原型(公有属性)链接在一起,在加上最后一条黑色的箭头引用到null,就是某一条颜色原型链。同样也说明Object的原型对象(__proto__)没有原型(共有属性)。
String对象包装器的MDN链接:
Stringdeveloper.mozilla.org![b1732b110a40f05dc19225039a572d9f.png](https://i-blog.csdnimg.cn/blog_migrate/c3902270a6f64f97293402c41721213d.jpeg)
Number对象包装器的MDN链接:
Numberdeveloper.mozilla.org![b1732b110a40f05dc19225039a572d9f.png](https://i-blog.csdnimg.cn/blog_migrate/c3902270a6f64f97293402c41721213d.jpeg)
Boolean对象包装器的MDN链接:
Booleandeveloper.mozilla.org![b1732b110a40f05dc19225039a572d9f.png](https://i-blog.csdnimg.cn/blog_migrate/c3902270a6f64f97293402c41721213d.jpeg)
4、prototype和__proto__
前面的博客有说明到,内存里面的对象必须要有引用才会存在,如果没有引用就会被当做垃圾回收。
bomber:《JS类型转换、内存图、GC、深/浅拷贝》zhuanlan.zhihu.com前面说到的__proto__公用属性(原型)是实例对象的隐藏属性,必须要创建声明并赋值后,得到的实例对象,或者某个信息可以被当做实例对象才会使用到,那如果没有创建,或者没有可以当做实例对象的信息是不是会被浏览器当做垃圾回收?
为了公有属性(原型)不被浏览器当做垃圾回收,于是浏览器自己内部有一个prototype属性,这个prototype是提供给浏览器自带的各种构造函数的属性使用的。
![45b3cc5c3f6ac80121ae5a1c73415bab.png](https://i-blog.csdnimg.cn/blog_migrate/e76c3c0df30ed9958faea22ee71c77a5.jpeg)
所以说__proto__是需要用户来输入代码,例如声明某个对象后,提供给用户需要使用原型对象的时候来使用的。
而prototype,是浏览器本身自带的,不需要用户输入任何代码,为了防止原型对象(公用属性或者原型所在的对象)被浏览器当做垃圾回收掉来使用的。
更深入的来探索,主要分三块——实例对象、原型对象(公用属性或者原型所在的对象)、对象的构造函数。
new关键字的MDN链接
new运算符developer.mozilla.org![b1732b110a40f05dc19225039a572d9f.png](https://i-blog.csdnimg.cn/blog_migrate/c3902270a6f64f97293402c41721213d.jpeg)
通过new 运算符我们可以创建一个用户定义的对象类型的实例
通过下面这个代码
function
通过new运算符创建具有构造函数的内置对象的实例
通过下面这个代码
var
上面的代码
Name是bomber函数构造的实例对象;
bomber是name这个对象的构造函数。
并且通过内置的构造函数,创建了包装成对象的number、string、boolean。
所以对象的构造函数和实例对象,我们自己也可以创建。
当然浏览器本身已经自带了很多构造函数,比如Number、Boolean、String、Object、Function。
而实例对象是否有浏览器本身自带的呢?因为我们前面学过JS里面的各种类型,除了undefined和null以外,还有对象作为类型,另外的数字、布尔、字符串不是对象,但是可以当做对象来使用。那么我们就可以说浏览器本身自带了实例对象或者一些可以被当做对象的对象。
只要是实例对象,或者可以被当做实例对象的都有__proto__属性,但是没有prototype属性。
只要是对象的构造函数都有prototype属性;
实例对象.__proto__===该实例对象的构造函数.prototype。
根据上面的六行代码和上面标粗的三句话列出表格归纳:
用户自己创建的实例对象:
![020d7f56862449e777e7be6bb8862cd9.png](https://i-blog.csdnimg.cn/blog_migrate/dab4a01a77ae0003f275baf242504c27.jpeg)
浏览器自带的,直接输入数字、字符串和布尔作为实例对象:
![2cfa3ed4561d99683d1dd288e4df8f5a.png](https://i-blog.csdnimg.cn/blog_migrate/85824180ad41ec66de7c567b3c79002f.jpeg)
构造函数既可作为对象,又可作为函数,所以他们既有__proto__属性,也有prototype属性
![e77fe40f00aff7b49e3d1153da512da3.png](https://i-blog.csdnimg.cn/blog_migrate/676c88a0295923bb7dccf68b621af250.jpeg)
上面表格中有注意两个等式
![602f847e12ea12cef17bf636151aa675.png](https://i-blog.csdnimg.cn/blog_migrate/eb93dcfff9b6c7c0e10c58427426e3bd.jpeg)
除了构造函数以外,其他都没有prototype属性,举几个例子。
![f5610b881f5b68780461c3809bacdf5f.png](https://i-blog.csdnimg.cn/blog_migrate/487a8e0099cde2774d2584fac00e27da.jpeg)
5、部分总结
(1)Global Object (全局对象或Global对象)
主要分类可以看第1节的表格。
(2)ECMAScript规定的函数(函数也属于对象)
通过new关键字和构造函数可以把非对象(字符串、数字、布尔)包装成一个具有该类型(字符串、数字、布尔)特征的对象,非对象(字符串、数字、布尔)只能通过临时对象来使用对应的属性和方法。举了很多例子,具体看第2节吧。
(3)公用属性(原型)
- 公用属性(原型)可以节省很多内存;
- 公用属性(原型)放在原型对象中,对象从开头把原型(公用属性)链接起来就是原型链;
(4)prototype和__proto__
第4节列了几个表格,经过表格分析后,我们可以知道
- 只要是实例对象,或者可以被当做实例对象的都有__proto__属性,但是没有prototype属性;
- 只要是对象的构造函数都有prototype属性;
- 实例对象.__proto__===该实例对象的构造函数.prototype;
- 构造函数既可作为对象,又可作为函数,所以他们既有__proto__属性,也有prototype属性;
- prototype,是浏览器本身自带的,不需要用户输入任何代码,为了防止原型对象(公用属性或者原型所在的对象)被浏览器当做垃圾回收掉来使用的;
- null是构造函数Object的原型对象作为实例对象的原型对象。说明Object.prototype就是最内层有用的原型对象,因为null没用,可能这也是typeof null返回的结果是Object的原因之一吧;
- Function.prototype是构造函数Function、String、Number、Boolean、Object作为实例对象的原型对象,说明构造函数Function创造了Function自己,也创造了其他所有构造函数(包括Object,此时Object是作为构造函数);
- Object.prototype是其他所有对象或者可以当做对象(例如Function、Function.prototype等,包括Object自己,此时Object是作为实例对象)的有用的最内层原型对象,而null是没用的,所以构造函数Object创造了所有对象。
- 前面两句可以总结为构造函数Function创造了所有构造函数(包括Object),而构造函数Object创造了所有对象,最后构造函数Function也可以作为实例对象。
其他链接:
以上的原型很像属性结构的根,如果对数据结构有一定了解,理解起来就比较容易:
bomber:算法和数据结构zhuanlan.zhihu.com构造函数或者构造方法(constructor)的MDN链接
constructordeveloper.mozilla.org![b1732b110a40f05dc19225039a572d9f.png](https://i-blog.csdnimg.cn/blog_migrate/c3902270a6f64f97293402c41721213d.jpeg)
构造函数Function的MDN链接
Functiondeveloper.mozilla.org![b1732b110a40f05dc19225039a572d9f.png](https://i-blog.csdnimg.cn/blog_migrate/c3902270a6f64f97293402c41721213d.jpeg)
函数Functions的MDN链接
函数developer.mozilla.org![b1732b110a40f05dc19225039a572d9f.png](https://i-blog.csdnimg.cn/blog_migrate/c3902270a6f64f97293402c41721213d.jpeg)
原型、原型链、原型对象、实例对象、构造函数说明见MDN链接:
继承与原型链developer.mozilla.org![b1732b110a40f05dc19225039a572d9f.png](https://i-blog.csdnimg.cn/blog_migrate/c3902270a6f64f97293402c41721213d.jpeg)
本文为本人的原创文章,著作权归本人和饥人谷所有,转载务必注明来源