可以通过Object.create(proto, [, propertiesObject])实现。详见:Object.create()
JS中,对象分普通对象和函数对象,Object、Function是JS自带的函数对象。凡是通过new Function()创建的对象都是函数对象,其他的都是普通对象。
typeof Object // "function", 函数对象
typeof Function // "function", 函数对象
function f1 () {}
var f2 = function () {}
var f3 = new Function('str', 'console.log(str)')
var o1 = new f1()
var o2 = {}
var o3 = new Object()
typeof f1 // "function", 函数对象
typeof f2 // "function", 函数对象
typeof f3 // "function", 函数对象
typeof o1 // "object", 普通对象
typeof o2 // "object", normal object
typeof o3 // "object", normal object
typeof Object // "function", 函数对象
typeof Function // "function", 函数对象
function f1 () {}
var f2 = function () {}
var f3 = new Function('str', 'console.log(str)')
var o1 = new f1()
var o2 = {}
var o3 = new Object()
typeof f1 // "function", 函数对象
typeof f2 // "function", 函数对象
typeof f3 // "function", 函数对象
typeof o1 // "object", 普通对象
typeof o2 // "object", normal object
typeof o3 // "object", normal object
每当定义一个对象(函数)时,对象中都会包含一些预定义的属性。其中,函数对象会有一个prototype属性,就是我们所说的原型对象(普通对象没有prototype,但有_proto_属性;函数对象同时含有prototype和__proto__属性)。
原型对象其实就是普通对象(Function.prototype除外,它是函数对象,单同时它又没有prototype属性)。
function f1 () {}
console.log(f1.prototype) // Object{} with two properties constructor and __proto__
typeof f1.prototype // "object"
typeof Object.proto
// 特例,没必要记住,平常根本用不到
typeof Function.prototype // "function"
typeof Function.prototype.prototype // "undefined"
typeof Object.prototype // "object"
function f1 () {}
console.log(f1.prototype) // Object{} with two properties constructor and __proto__
typeof f1.prototype // "object"
typeof Object.proto
// 特例,没必要记住,平常根本用不到
typeof Function.prototype // "function"
typeof Function.prototype.prototype // "undefined"
typeof Object.prototype // "object"
原型对象的主要作用是用于继承:
var Person = function (name) {
this.name = name
}
Person.prototype.getName = function () {
return this.name
}
var yakima = new Person('yakima')
yakima.getName() // "yakima"
var Person = function (name) {
this.name = name
}
Person.prototype.getName = function () {
return this.name
}
var yakima = new Person('yakima')
yakima.getName() // "yakima"
上面提到原型对象的主要作用是用于继承,其具体的实现就是通过原型链实现的。创建对象(不论是普通对象还是函数对象)时,都有一个叫做__proto__的内置属性,用于指向创建它的函数对象的原型对象(即函数对象的prototype属性)
yakima.__proto__ === Person.prototype // true,对象的内置__proto__对象指向创建该对象的函数对象的prototype
Person.prototype.__proto__ === Object.prototype // true
// 继续,Object.prototype对象也有__proto__属性,但它比较特殊,为null
Object.prototype.__proto__ === null // true
typeof null // "object"
yakima.__proto__ === Person.prototype // true,对象的内置__proto__对象指向创建该对象的函数对象的prototype
Person.prototype.__proto__ === Object.prototype // true
// 继续,Object.prototype对象也有__proto__属性,但它比较特殊,为null
Object.prototype.__proto__ === null // true
typeof null // "object"
这个由__proto__串起来的直到Object.prototype.proto ==> null对象的链称为原型链。
说明(下面这几种看完忘掉就可以了^_^)
Object是函数对象,是通过new Function ()创建的,所以Object.__proto__指向Function.prototype
Object.__proto__ === Function.prototype // true
Object.__proto__ === Function.prototype // true
Function是函数对象,是通过new Function()创建的,所以Function.__proto__指向Function.prototype。本类创建本类。。。唐僧也说过类似的话的,人是人他妈生的,妖是妖他妈生的。
Function.__proto__ === Function.prototype // true
Function.__proto__ === Function.prototype // true
另外:
Function.prototype.__proto__ === Object.prototype // true
Function.prototype.__proto__ === Object.prototype // true
原型对象中都有个constructor属性,用来引用它的函数对象。这是一种循环引用。
Person.prototype.constructor === Person // true
Function.prototype.constructor === Function // true
Object.prototype.constructor === Object // true
Person.prototype.constructor === Person // true
Function.prototype.constructor === Function // true
Object.prototype.constructor === Object // true
原型和原型链是JS实现继承的一种模型。
var Animal = function () {}
var Dog = function () {}
Animal.price = 2000
Dog.prototype = Animal
var tidy = new Dog()
console.log(Dog.price) // undefined
console.log(tidy.price) // 200
var Animal = function () {}
var Dog = function () {}
Animal.price = 2000
Dog.prototype = Animal
var tidy = new Dog()
console.log(Dog.price) // undefined
console.log(tidy.price) // 200
对上例的分析:
if (typeof localStorage !== 'undefined') {
// 此时访问localStorage不会出现引用错误
}
if (typeof localStorage !== 'undefined') {
// 此时访问localStorage不会出现引用错误
}
或者
if ('localStorage' in self) { // 浏览器端全局处window/this/self三者彼此全等
// 此时访问 localStorage 绝对不会出现引用错误
}
if ('localStorage' in self) { // 浏览器端全局处window/this/self三者彼此全等
// 此时访问 localStorage 绝对不会出现引用错误
}
注意二者的区别:
var a // 或 var a = undefined
'a' in self // true
typeof a // 'undefined'
var a // 或 var a = undefined
'a' in self // true
typeof a // 'undefined'
function isObjectEqual (obj1, obj2) {
if (typeof obj1 !== 'object' || typeof obj2 !== 'object') {
return obj1 === obj2
}
// if refer to the same location
if (obj1 === obj2) {
return true
}
var keys1 = Object.keys(obj1)
var keys2 = Object.keys(obj2)
if (keys1.length !== keys2.length) {
return false
}
if (keys1.length === 0 && keys2.length === 0) {
return true
}
for (var i = 0, len = keys1.length; i < len; i++) {
if (!isObjectEqual(obj1[keys1[i]], obj2[keys2[i]])) {
return false
}
}
return true
}
function isObjectEqual (obj1, obj2) {
if (typeof obj1 !== 'object' || typeof obj2 !== 'object') {
return obj1 === obj2
}
// if refer to the same location
if (obj1 === obj2) {
return true
}
var keys1 = Object.keys(obj1)
var keys2 = Object.keys(obj2)
if (keys1.length !== keys2.length) {
return false
}
if (keys1.length === 0 && keys2.length === 0) {
return true
}
for (var i = 0, len = keys1.length; i < len; i++) {
if (!isObjectEqual(obj1[keys1[i]], obj2[keys2[i]])) {
return false
}
}
return true
}
ES6引入了Generator函数,示例如下:
function* hello (name) {
yield `hello ${name}!`
yield 'I am glad to meet you!'
if (0.6 > 0.5) {
yield `It is a good day!`
}
yield 'See you later!'
}
// Generator函数执行后会返回一个迭代器,通过调用next方法依次yeild相应的值
var iterator = hello('Yakima')
iterator.next() // 返回{value: "hello Yakima!", done: false}
iterator.next() // 返回{value: "I am glad to meet you!", done: false}
iterator.next() // 返回{value: "It is a good day!", done: false}
iterator.next() // 返回{value: "See you later!", done: false}
iterator.next() // 返回{value: undefined, done: true}
iterator.next() // 返回{value: undefined, done: true}
function* hello (name) {
yield `hello ${name}!`
yield 'I am glad to meet you!'
if (0.6 > 0.5) {
yield `It is a good day!`
}
yield 'See you later!'
}
// Generator函数执行后会返回一个迭代器,通过调用next方法依次yeild相应的值
var iterator = hello('Yakima')
iterator.next() // 返回{value: "hello Yakima!", done: false}
iterator.next() // 返回{value: "I am glad to meet you!", done: false}
iterator.next() // 返回{value: "It is a good day!", done: false}
iterator.next() // 返回{value: "See you later!", done: false}
iterator.next() // 返回{value: undefined, done: true}
iterator.next() // 返回{value: undefined, done: true}
Generator函数与常见的函数的差异:
常见的函数不能暂停执行,而Generator函数可以,这是两者最大的区别。
写出下面代码的执行结果:
// 当前位于全局作用域下
function testObject () {
alert(this)
}
testObject()
// 当前位于全局作用域下
function testObject () {
alert(this)
}
testObject()
上题的答案:在chrome中会弹出[object Window]
var msg = 'String A'
function test () {
alert(msg)
var msg = 'String A'
alert(msg)
}
test()
var msg = 'String A'
function test () {
alert(msg)
var msg = 'String A'
alert(msg)
}
test()
上题的分析与答案:在函数内部声明的变量在函数内部会覆盖掉全局同名变量。在JS预解析时,定义变量的行为会在变量作用域内的顶部实现(hoisting),但是变量的赋值行为并不会提前,所以上述代码等价于如下代码,所以第一次alert弹出的是undefined,第二次alert弹出的是“String A”。
var msg = 'String A'
function test () {
var msg
alert(msg)
msg = 'String A'
alert(msg)
}
var msg = 'String A'
function test () {
var msg
alert(msg)
msg = 'String A'
alert(msg)
}
写出下面代码a、b、c三行的输出分别是什么?
// mark A
function fun (n, o) {
console.log(o)
return {
// mark B
fun: function (m) {
// mark C
return fun(m, n)
}
}
}
var a = fun(0); a.fun(1); a.fun(2); a.fun(3)
var b = fun(0).fun(1).fun(2).fun(3)
var c = fun(0).fun(1); c.fun(2); c.fun(3)
// 答案:
// undefined, 0, 0, 0
// undefined, 0, 1, 2
// undefined, 0, 1, 1
// mark A
function fun (n, o) {
console.log(o)
return {
// mark B
fun: function (m) {
// mark C
return fun(m, n)
}
}
}
var a = fun(0); a.fun(1); a.fun(2); a.fun(3)
var b = fun(0).fun(1).fun(2).fun(3)
var c = fun(0).fun(1); c.fun(2); c.fun(3)
// 答案:
// undefined, 0, 0, 0
// undefined, 0, 1, 2
// undefined, 0, 1, 1
首先,可以分析得到的结论:标记A下面的fun函数和标记C下面return的fun是同一个函数,标记B下面的fun属性对应的函数不同于标记A和标记C下方的函数。下文为了行文方便,将各个标记处下方的函数方便叫做A、B、C函数。
a行的分析:
b行的分析:
c行的分析:
mark:
只回答了浅拷贝,不知道为什么
面试官提示我去转换一下代码看看它在 es5 里面是怎么样