浅谈JS原型

JS原型学习笔记,如有错误,还请留言~~

  • 全局对象window
  • 什么是原型
  • JS中一切皆为对象?

全局对象window

在谈window之前,试想一个简单的问题,打开浏览器在控制台输入console.log("Hello");
并按下回车键,我们理所应当看到了控制台给我们返回的结果Hello,那么,console.log()这个方法是怎么来的呢?其实浏览器中已经为我们内置了一个全局对象叫做window,window作为全局对象,代表脚本正在运行的浏览器窗口。console.log()是一种简写的格式,更加精确的写法为:window.console.log()。window下则封装了多种 多样的属性,这些属性中,有些是浏览器自带的属性,有些则是ECMAScript标准规定的属性。ECMAScript规定的全局对象为global,但是在浏览器下则是window。

image



这里需要注意的是,浏览器中的全局对象是window,但是在其他的环境下,例如node.js就不一样了。浏览器下,在控制台输入window可以查看全局对象的所有属性。而在node.js环境中,则需要使用global,且浏览器中规定的属性在node.js环境下自然是无效的。


当我们打开浏览器,系统自然会为浏览器分配内存,浏览器将内存分给各个页面,页面中的HTML,CSS,JS,插件等会分配到一部分内存空间。对于JS来说,一开始window对象就已经存在于堆内存中了。

image

什么是原型

先上图:

image



这是堆内存中window全局对象的部分内存模型图,红色框部分为函数,黑色框部分为prototype对象,先不管函数中的prototype属性以及对象中的proto属性的含义。我们先声明一个对象,并调用toString()方法,看一看内存中到底会发生什么。

var obj = new Object();

image



我们看到我们在声明了一个空对象之后,内部自带了一个proto属性,且使用命令:obj.__proto__ === Object.prototype 结果会返回true


image



那么也就是说:

image



obj对象的proto属性指向了Object.prototype这样一个对象。我们来看一看Object.prototype里面有什么。

image



在Object.prototype中的toString属性指向一个函数,恰恰是我们想要使用的toString()这样一个方法。现在我们在大概可以明白:在我们声明一个对象obj时,这明明是一个空对象,但是这个对象却可以调用toSting(),valueOf()等方法,是因为在我们声明对象时,浏览器为我们这个对象添加了一个属性__proto__,这个属性指向了一个Object.prototype这样一个对象,Object.prototype对象中有着Object所共有的一些属性。其实这个prototype就是原型,比起原型,我更喜欢叫它共有属性对象。那么__proto__是什么呢?__proto__是浏览器自动赋予对象的一个属性,这个属性指向着一个函数的原型,当然最后的root必然是Object.prototype。


例如:

var n = new Number(15);
n.toString(16);// "f"

上述代码的含义是,将数值类型的n转化为16进制后,再将其转化为字符串,15在16进制中对应的值为f,转化为字符串之后的结果为"f"。很显然,Object原型中的toString()方法是没有办法将一个值按照进制转化,再变为字符串的,也就是说,Number类型的toString(),不同于Object原型的toString()方法。倘若是Java,我们会想到方法的重写,但是JS则不一样,我们继续从内存模型的角度来分析到底发生了什么:

image



首先var n = new Number(15); n为一个对象,如果不明白为什么n的值会在堆内存中,可以参考我的文章JS内存模型。我们可以看到,对象n的__proto__指向了Number.prototype,在Number.prototype中有什么呢?

image



我们可以看到,Number.prototype也有一个toString的属性,指向了toString()这样一个函数。并且

image



Number.prototype.toString === Object.prototype.toString返回的结果为false,也就是说,Number.prototype中的toString是一个“重写”的属性,当我们声明了对象n,并调用toString()方法时,首先,浏览器会在n这个对象中寻找toString这个属性,如果没有它就会在n这个对象的__proto__属性所指向的原型中寻找,n.__proto__指向了Number.prototype。 因为Numebr.prototype即Number原型也是一个对象,Number.prototype.__proto__则指向了root即Object.prototype。如果在Number原型中没有toString这个属性,那么顺其自然地就会在Object原型中寻找这个属性。当然本例中,在Number的原型中Number.prototype找到了toString这个属性,自然会调用它所指向的Number独有的toString()方法。刚刚描述的过程中,这种指向的关系好似链表,而在JS中,我们可以形象地称作为“原型链”。


接下来,我们再思考一个问题:函数是对象吗 ?虽然typeof一个函数返回的结果为“function”,但是我们一再强调JavaScript里的数据类型只有七种,无论是数组还是函数,它们的本质都是对象。既然明确了函数是一个对象,那么在上文中,我曾经暗示prototype是一个函数所拥有的属性,proto是一个对象所拥有的属性,那么函数既然是对象,那么换言之,一个函数中自然拥有两种属性了。我们再将内存模型图完善一些:

image



在上图中我们看到,如果一个函数作为一个函数而言,它自身的属性为prototype,这个属性指向它所对应的原型。如果将一个函数作为对象来看,它的属性proto则指向了Function.prototype,这种指向的关系仔细想一想也合情合理,一个函数会提供自己的共有属性,但是一个函数本质上也是一个对象,它被构造出来需要一个构造函数。值得一提的是Funcion.__proto__ === Funciton.prototype。 这可以形象地理解为:自己造自己,自己赋予自己了属性。其实理解不了上图也无关紧要,我们只需要记住原型里面的一个规则即可:

内置函数.__proto__ === Function.prototype;

一切皆为对象?

JS中一切皆为对象?很显然这句话是错的,用最简单的代码就可以证明:

var n = 15;
typeof n;// "number"

JS中,有七种数据类型,在上面的代码中,我们声明了var n = 15;,在使用typeof n时,我们也可以看到返回的类型是number。但是,我们却可以这样做:

n.toString(16);// "f"

和上面介绍的var n = new Number(15)声明n的方式不同。n的typeof 返回的是一个“number”,如果使用var n = new Number(15)声明n,那么使用typeof n返回的结果就会是“object”。在声明一个对象时,浏览器会为这个对象自动添加__proto__这样一个属性指向原型,好调用相应的方法,既然我们声明了var n = 15;且可以调用toString()这样一个方法,那不还是说明n实际上是一个对象吗?实际上,并不是这样的,n自然是一个number类型的数字,只不过这里面另有蹊跷。

var n = 15;
n.toString(16);

当我们调用toString()方法时,在堆内存中会生成一个临时对象,我们暂时叫它temp。也就是说这个过程是这样的:

var n = 15;
// 调用toString()方法时,会在堆内存中产生一个临时变量temp
// var temp = new Number(n);
// 将temp.toString(16)的结果记录下来
// 临时变量temp随即被"抹杀掉"

一个数值型,字符串型等普通类型的变量可以调用原型中的方法,并不能说明它们类型的本质是对象,因为“创建临时变量机制”,让许多人对此有了误解,实际上数值型即是数值型,字符串型是字符串型,JS当中一切皆对象很显然是一个谬论。再看一个例子:

var a = 1;
// 问:执行此条语句 a.xxx = 2 是否会执行成功? 

其实只要理解了"临时对象"这个概念,就不难回答这个问题,在声明var a = 1;后,我们已经确定了a的类型是Number,在执行语句a.xxx = 2;时,在堆内存中会生成一个临时对象,对于这个临时对象来说,执行a.xxx = 2;就相当于添加了一个属性xxx其值为2,所以这条语句自然能执行成功。当然这个临时对象会立刻被“抹杀”,当我们再次在控制台输入a.xxx查看这个值时,返回的结果自然是undefined。

image


©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,829评论 1 331
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,603评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,846评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,600评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,780评论 3 272
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,695评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,136评论 2 293
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,862评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,453评论 0 229
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,942评论 2 233
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,347评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,790评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,293评论 3 221
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,839评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,448评论 0 181
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,564评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,623评论 2 249

推荐阅读更多精彩内容

  • 概要 64学时 3.5学分 章节安排 电子商务网站概况 HTML5+CSS3 JavaScript Node 电子...
    阿啊阿吖丁阅读 8,582评论 0 3
  • 关于JS面向对象,我们首先需要了解的一个问题,就是什么是内存! var a = 1 发生了什么? 我们常说的内存...
    si_月阅读 317评论 0 0
  • 第3章 基本概念 3.1 语法 3.2 关键字和保留字 3.3 变量 3.4 数据类型 5种简单数据类型:Unde...
    RickCole阅读 5,000评论 0 21
  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5? 答:HTML5是最新的HTML标准。 注意:讲述HT...
    kismetajun阅读 27,071评论 1 45
  • 学习使用过js的人一开始都会觉得js简单,这是因为js语法简单,学习过编程语言的人,很容易掌握js的基本语法并按要...
    易则知阅读 1,284评论 0 11