对象,原型与原型链

全局对象

ECMAScript 规定全局对象叫做 global,但是浏览器把 window 作为全局对象(浏览器先存在的)

window 就是一个哈希表,有很多属性。

window 的属性就是全局变量。

在浏览器下, window 的属性可以分为两类:

  • ECMAScript 规定的,例如 parseInt()parseFloat()Number()String()Boolean()setTimeout() 等等
  • 浏览器私有属性,例如 alert 弹框提示 , propmt 用户填写 , confirm 确认, console 等,document 也是。 DOM也有标准,由 W3C 制定。

Number()

Number() 有两种用法:

  1. Number(xxx) 用来把 xxx 转换为数字
  2. 声明一个 number 对象,包装成对象,有很多便捷的操作可以用

    var n = new Number(1)
    n.valueOf()
    // 1
    n.toString()
    // "1"
    n.toFixed(3)
    // "1.000"
    n.toExponential()
    "1e+0"
    

但是在 Javascript 中,我们很少用第二种方法。

你有没有想过,为什么 var a = 1 仅仅是声明了一个基本类型,基本类型没有属性,但是却有很多操作可以直接用呢?

var a = 1
a.toFixed(3)
// "1.000"
a.toString()
// "1"
a.valueOf()
// 1

原因在于这其中涉及到了临时转换。

在声明的时候,JavaScript 创建了临时对象,上面记录了一些可以使用的操作,在语句结束之后,临时对象会消失。

var b = 1
b.xxx = 2  //看到 b ,JS马上创建了临时对象,语句结束后,临时对象消失
b.xxx  //此时又看到b,JS马上创建了新的临时对象。。
// undefined

看下面的内存图:

临时转换
临时转换

var n=1;
n.toString() ==> temp=new Number(n);
temp.toString() //用完后把temp对象干掉

String()

  1. String(xxx) 用来把 xxx 转换为字符串
  2. 声明一个 string 对象,有很多便捷的操作可以用

    var s = 'Jonathan'
    var s2 = new String('Hello!')
    

建议在 Chrome 控制台输入观察一下。

charAt() 和 charCodeAt()

var s = 'Jonathan'
var s2 = new String('Hello!')
s2.charAt(0)  //返回某个索引对应的字符
// "H"
s2.charCodeAt(0)  //返回某个索引对应的字符的 unicode 编码
// 72
'a'.charCodeAt()
// 97
'a'.charCodeAt(0)
// 97
'a'.charCodeAt().toString(16)
// "61"  //a的十六进制的 unicode 是 "61"

string 还有很多很多 API:

  • trim()
  • concat()
  • slice()
  • replace()
  • ……

Boolean()

  1. Boolean(xxx)
  2. 声明一个 Boolean 对象,有很多便捷的操作可以用

    var f = false
    var f2 = new Boolean(false)
    if (f) {console.log(1)}
    if (f2) {console.log(2)}
    // 2
    

Object()

不同于基本类型,Object 这两种声明方法没有区别。但是需要注意,这两者并不相等。

var o = {}
var o2 = new Object()  // 不常用
o === o2
// false

公用的属性

  1. 避免浪费内存
  2. 当声明一个对象时,生成一个隐藏的key __proto__ ,其 value 指向公用属性。

所有对象都有 toString 和 valueOf 属性,那么我们是否有必要给每个对象一个 toString 和 valueOf 呢?

明显不需要。

JS 的做法是把 toString 和 valueOf 放在一个对象里(暂且叫做公用属性组成的对象)

然后让每一个对象的 __proto__ 存储这个「公用属性组成的对象」的地址。

重要公式

var 对象 = new 函数()
对象.__proto__ === 函数.prototype

请记住并疯狂思考下面这张图

原型与原型链
原型与原型链

不妨大胆地思考一下:

// 声明一个对象 obj ,它有自己的属性 name 和 toString:
var obj = {
    name: 'Jonathan',
    toString: function() {
        return '666'
    }
}
// obj 的原型 obj.__proto__ 有很多属性,比如:toString 和 valueOf
// 最后,obj.__proto__.__proto__ 是 null
// 这就是原型链的末尾,即 null
// null 没有 __proto__
// 综上,整个原型链如下:
// obj ---> obj.__proto__ ---> null

console.log(obj.name)  // Jonathan
// name 是 obj 自身的属性,值为 Jonathan

console.log(obj.toString)  // f() { return '666'}
// toString 是 obj 自身的属性
// obj.__proto__ 上还有一个 toString 属性,但是不会被访问到。
// 这种情况称为 属性遮蔽(property shadowing)

console.log(obj.valueOf)  // ƒ valueOf() { [native code] }
// valueOf 并非 obj 自身的属性,那就继续找找 obj.__proto__

console.log(obj.xxx)  // undefined
// xxx 是 obj 的自身属性吗?不是,那看看 o.__proto__ 上有没有
// xxx 是 o.__proto__ 的自身属性吗?不是,那看看 o.__proto__.__proto__ 上有没有
// o.__proto__.__proto__为null,停止搜索
// 没有 xxx 属性,返回 undefined

学习资料:

window MDN

什么是 JS 原型链?

(完)