ECMAScript规定全区对象叫做global,但是浏览器把window作为全局对象(浏览器先存在)window就是一个哈希表,有很多属性,window的属性就是全局变量。
window下面的属性分为两类,第一类是ECMAScript规定必须有的函数,第二类是私有的(浏览器专有,标准不一)。
简单类型与对象的区别:
//全局函数
//1.Number
var n = new Number(1)//创建一个Number对象
1与new Number(1)的区别是什么?看内存图
//2.String
var s = new String('hello')//创建一个Number对象
'hello'与new String('hello')的区别是什么?看内存图
//3.boolean
var b = new Boolean(true)//创建一个Number对象
true与new Boolean(true)的区别是什么?看内存图
//4.Object
var o1 = {}
var o2 = new Object()
o1和o2没区别
简单数据类型和通过new 构造函数创建的数据对象(除了对象)区别在于内存地址不同,前者存放在stack中,而后者存放在heap里。
将数值通过
Number
包装成对象,内存里面就有许多操作该数值的方法。但简单数据也可以调用这些办法,那为什么还需要将数值包装成对象呢?答:历史原因,在发明JS时模仿JAVA声明数据(
new
关键字,满足老板需求),第二种可以直接声明var a =1
。 由于第二种方法无法使用toString
等方法(只有对象才能使用方法,简单数据类型不可以),创始人使用了一个妙计(临时转换)当写n.toString
时,隐式的声明一个temp为 new Number(n)
,然后再调用n.toString的值为temp
,再销毁temp数据。临时的转换,用完就销毁了。所以给简单数据类型加属性读取时将会报错,这种方法受到了开发人员的欢迎。公有的属性藏在哪?
所有的对象都有toString
和valueOf
属性,那么我们是否有必要给每个对象一个toString
和valueOf
呢?
明显不需要(内存不允许),JS的做法是把toString
和valueOf
放在一个对象里,暂且叫做公有属性组成的对象(原型)。
对象分为普通对象和函数对象,
Object
和Function
是js自带的函数对象,每个对象都有原型(null和undefined除外)可以理解为对象的默认属性和方法。
字符串对象
通过new string()
方法转换成的字符串对象是一个哈希,每一个字符对应相应的索引值,使用方括号或者charAt(索引值)
来获取相对应的字符,charCodeAt(索引值)
获取对应索引的值的编码。继承了String
对象的方法。
获取一个数字对应的进制值使用toString()
方法,如(100).toString(16)//64
可以将字符的编码转化为其他进制的编码
布尔值对象
简单数据类型false和布尔对象false在被转换时是不相等的。
为什么不相等?虽然两个对象都没有名字,但存放在heap中的地址值不同,自然不会相等,请牢记5个假值,对象经过布尔转换为真值。
结论:所有新声明的对象都是不相等的。除非将一个对象的地址值赋值给另一个变量。
Number
String
Boolean
Object
通过以上几种方式创建的对象都具有toString
、valueof
方法,这两个方法哪里来的呢?如果在每个新生成对象里面都定义这两个方法太占内存。实现继承的方法:原型链
要清楚原型链,首先先弄清楚对象。
普通对象,有____proto____
属性(指向其原型链),没有prototype属性。
原型对象,构造函数.prototype原型对象还有constructor属性指向构造函数对象
函数对象,通过new Function()创建的都是函数对象。用有prototype
、____proto____
属性(指向原型对象)
原型链概念
ECMAScript中描述了原型链的机制,将原型链作为实现继承的主要方式。其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。
在js中,每个对象都有一个指向它的原型对象的内部链接,这个原型对象又有自己的原型,直到某个对象的原型为null为止(也就是不再有原型指向),组成这条链的最后一环。这种一级一级的链接结构就称为原型链。
JS对象有一个指向一个原型对象的链,当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象原型的原型,依此层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。
所有引用类型默认都继承了
Object
,而这个继承也是通过原型链实现的,所有函数的默认原型都是Object
的实例,因此默认原型都会包含一个内部指针,指向Object.prototype
。这也正是所有自定义类型都会继承toString()
,valueOf()
等默认方法的根本原因,所有函数的原型最顶端都有一个最终的对象原型,存储着对象本身自带方法和属性,当调用实例上的toString()
等方法时,实际上是调用的是保存在Object.prototype
中的那个方法。
简单回顾一下构造函数、原型和实例之间的关系:每创建一个函数都有一个prototype属性指向通过该构造函数创建实例对象的原型对象,原型对象是包含特定类型的所有实例共享的属性和方法,都包含了一个指向构造函数的指针,而实例都包含一个指向原型对象的____proto____
内部指针。
下面是一个简单的例子:
//原型链实例
//构造函数Animal
function Animal(){
this.type = "animal"
}
//Animal构造函数定义原型方法
Animal.prototype.getType = function(){
return this.type
}
//Dog构造函数
function Dog(){
this.name = 'dog'
}
//Dog构造函数定义原型方法
Dog.prototype.getName = function(){
return this.name
}
//Dog原型等于构造函数Animal实例(实现继承)
Dog.prototype = new Animal()
//构建新实例
var xiaohuang = new Dog()
实例、构造函数、原型之间的关系。
xiaohuang.__proto__ === Dog.prototype
Dog.prototype.__proto__ === Animal.prototype
Animal.prototype === Object.prototype
Object.prototype.proto === null
//总结:xiaohuang这个Dog的实例继承了Animal,Animal继承了Object
console.log(xiaohuang.type)//'animal'
console.log(xiaohuang.name)//'dog'
总结:
两个构造函数,将其中一个构造函数的实例赋值给另一个构造函数的原型,这样另一个原型就有了指向前面那一个构造函数的原型的指针(创建的实例也会有同样的原型链),查找属性和方法会根据原型链来进行搜索,先搜索实例,再搜索原型,最后是原型的原型.....,注意此时实例的constructor指向了最开始的构造函数,而不是创建实例的函数(为了代码的严谨,可以设置为创建此实例的函数)。实现的本质是重写原型对象,代之以一个新类型的实例。原型链继承不仅会继承原型上面的属性和方法,还会继承实例上的方法和属性。
Object原型对象,怎么让每个对象都指向这个共有属性?
答:每个对象都有一个__proto__
属性指向了共有属性。
对象和数值对象:
var o1 = new Number(1);
var o2 =new Object(1);
o1===o2 //false
o1.toString()===o2.toString() //true
两个数值对象不相等,因为stack存放的heap地址值不同,但使用的toString
函数是公共属性(Number的原型的属性),因此相等(new Object(1)
自动转换成了数值对象)。
Object()函数不加new操作符会根据传入的参数生成相应的数据类型的对象,对于Object函数来说,加不加new都是一样的效果。
String()函数不加new操作符是将你给定的参数变成string常量(基本类型,非对象),而加上new会生成String对象(复杂类型,字符串对象),除了Object函数以外,其他的标准库函数都是一样的效果。
数值的toString()
方法和对象的toString()
方法并不相等,因为前者可以加进制参数而且会被先搜寻到。
调用方法时,会经过一层一层的查找。
Number String Boolean Object
都有自己的共有属性,____proto____
都是先指向了自己的共有属性,共有属性的__proto____
再指向了对象。如果对象的共有属性没有被引用的话,必将会被回收,
object.prototype
指向(引用)这个共有属性(原型)当你声明一个对象时,JS引擎除了在栈内存里面存放一个hash之外,还将
____proto____
指向了你该有的共有属性(原型)。
不写代码就有prototype
在没有代码的时候,Number.prototype
、Object.prototype
、String.prototype
、Boolean.prototype
都引用了各自的共有属性(保证不被垃圾回收机制回收),是JS定义的,不可更改的。而你的对象____proto____
则指向了对应数据类型的prototype
。
初始化数据类型里面还有一个Function.prototype
,存储了函数的原型(共有方法),Function
也是一个对象,Function.__proto__
指向了Function.prototype
,使用函数初始化对象时(new Function()
),该函数对象的____proto____
就指向了Function.prototype
(因为Function是Object的构造函数),而Function.prototype
里面的____proto____
就指向了Object.prototype
。
继承方式:子类构造函数.prototype= new 父类构造函数 ,简单说就是将父类的实例赋值给子类的原型,这样子类的____proto____
属性
目前组合继承用的最多,构造函数模式定义实例属性(不可共享),原型模式则定义方法和共享的属性。
现在你明白什么是原型和原型链了吗?:)