浅谈JS原型

栏目: JavaScript · 发布时间: 6年前

内容简介:JS原型学习笔记,如有错误,还请留言~~在谈window之前,试想一个简单的问题,打开浏览器在控制台输入这里需要注意的是,浏览器中的全局对象是window,但是在其他的环境下,例如node.js就不一样了。浏览器下,在控制台输入

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

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

全局对象window

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

浅谈JS原型

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

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

浅谈JS原型

什么是原型

先上图:

浅谈JS原型
这是堆内存中window全局对象的部分内存模型图,红色框部分为函数,黑色框部分为prototype对象,先不管函数中的prototype属性以及对象中的__proto__属性的含义。我们先声明一个对象,并调用 toString()

方法,看一看内存中到底会发生什么。

var obj = new Object();
复制代码
浅谈JS原型

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

浅谈JS原型

那么也就是说:

浅谈JS原型

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

浅谈JS原型

在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则不一样,我们继续从内存模型的角度来分析到底发生了什么:

浅谈JS原型

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

浅谈JS原型

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

浅谈JS原型

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__是一个对象所拥有的属性,那么函数既然是对象,那么换言之,一个函数中自然拥有两种属性了。我们再将内存模型图完善一些:

浅谈JS原型
在上图中我们看到,如果一个函数作为一个函数而言,它自身的属性为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。

浅谈JS原型

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Data Structures and Algorithms

Data Structures and Algorithms

Alfred V. Aho、Jeffrey D. Ullman、John E. Hopcroft / Addison Wesley / 1983-1-11 / USD 74.20

The authors' treatment of data structures in Data Structures and Algorithms is unified by an informal notion of "abstract data types," allowing readers to compare different implementations of the same......一起来看看 《Data Structures and Algorithms》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

html转js在线工具
html转js在线工具

html转js在线工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具