JS原型学习笔记,若有错误,还请留言~~node
在谈window以前,试想一个简单的问题,打开浏览器在控制台输入console.log("Hello");
并按下回车键,咱们理所应当看到了控制台给咱们返回的结果Hello
,那么,console.log()
这个方法是怎么来的呢?其实浏览器中已经为咱们内置了一个全局对象叫作window,window做为全局对象,表明脚本正在运行的浏览器窗口。console.log()
是一种简写的格式,更加精确的写法为:window.console.log()
。window下则封装了多种 多样的属性,这些属性中,有些是浏览器自带的属性,有些则是ECMAScript标准规定的属性。ECMAScript规定的全局对象为global,可是在浏览器下则是window。
数组
window
能够查看全局对象的全部属性。而在node.js环境中,则须要使用
global
,且浏览器中规定的属性在node.js环境下天然是无效的。
先上图:
浏览器
toString()
方法,看一看内存中到底会发生什么。
var obj = new Object();
复制代码
obj.__proto__ === Object.prototype
结果会返回
true
。
__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则不同,咱们继续从内存模型的角度来分析到底发生了什么:
bash
var n = new Number(15);
n为一个对象,若是不明白为何n的值会在堆内存中,能够参考个人文章
JS内存模型。咱们能够看到,对象n的
__proto__
指向了Number.prototype,在Number.prototype中有什么呢?
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__是一个对象所拥有的属性,那么函数既然是对象,那么换言之,一个函数中天然拥有两种属性了。咱们再将内存模型图完善一些:
Funcion.__proto__ === Funciton.prototype
。 这能够形象地理解为:本身造本身,本身赋予本身了属性。其实理解不了上图也可有可无,咱们只须要记住原型里面的一个规则便可:
内置函数.__proto__ === Function.prototype;
复制代码
JS中一切皆为对象?很显然这句话是错的,用最简单的代码就能够证实:函数
var n = 15;
typeof n;// "number"
复制代码
JS中,有七种数据类型,在上面的代码中,咱们声明了var n = 15;
,在使用typeof n
时,咱们也能够看到返回的类型是number
。可是,咱们却能够这样作:post
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。也就是说这个过程是这样的:spa
var n = 15;
// 调用toString()方法时,会在堆内存中产生一个临时变量temp
// var temp = new Number(n);
// 将temp.toString(16)的结果记录下来
// 临时变量temp随即被"抹杀掉"
复制代码
一个数值型,字符串型等普通类型的变量能够调用原型中的方法,并不能说明它们类型的本质是对象,由于“建立临时变量机制”,让许多人对此有了误解,实际上数值型便是数值型,字符串型是字符串型,JS当中一切皆对象很显然是一个谬论。再看一个例子:prototype
var a = 1;
// 问:执行此条语句 a.xxx = 2 是否会执行成功?
复制代码
其实只要理解了"临时对象"这个概念,就不难回答这个问题,在声明var a = 1;
后,咱们已经肯定了a的类型是Number,在执行语句a.xxx = 2;
时,在堆内存中会生成一个临时对象,对于这个临时对象来讲,执行a.xxx = 2;
就至关于添加了一个属性xxx其值为2,因此这条语句天然能执行成功。固然这个临时对象会马上被“抹杀”,当咱们再次在控制台输入a.xxx查看这个值时,返回的结果天然是undefined。
插件