手写系列
代码不是背出来的。
JavaScript原生
new 操作符
当代码
new Foo(...)
执行时,会发生以下事情:
- 一个继承自
Foo.prototype
的新对象被创建。- 使用指定的参数调用构造函数
Foo
,并将this
绑定到新创建的对象。new Foo
等同于new Foo
()
,也就是没有指定参数列表,Foo
不带任何参数调用的情况。- 由构造函数返回的对象就是
new
表达式的结果。如果构造函数没有显式返回一个对象,则使用步骤1创建的对象。(一般情况下,构造函数不返回值,但是用户可以选择主动返回对象,来覆盖正常的对象创建步骤)
初级版
function _new(fn, ...args) {
// 1. 创建一个空的简单JavaScript对象(即`{}`);
let obj = new Object()
// 2. 链接该对象(即设置该对象的__proto__)到fn的原型对象 ;
obj.__proto__ = fn.prototype
// 3. 将步骤1新创建的对象作为`this`的上下文 ;
// 使用apply,改变构造函数的this指向,使其
// 指向新对象,这样,obj就可以访问到构造函数中的属性了。
fn.apply(obj, args)
// 4. 返回`this`。
return obj
}
function Person(name, age) {
this.name = name
this.age = age
}
let asuna = _new(Person, 'Asuna', 20)
let kirito = new Person('kirito', 21)
console.log('自己的new', asuna)
console.log('原生的new', kirito)
由构造函数返回的对象就是
new
表达式的结果。如果构造函数没有显式返回一个对象,则使用步骤1创建的对象。(一般情况下,构造函数不返回值,但是用户可以选择主动返回对象,来覆盖正常的对象创建步骤)
初级版代码会产生一个问题
// 函数返回对象
function Animal(name, sex) {
this.type = 'animal'
return { name, sex }
}
function _new(fn, ...args) {...}
let cat = new Animal('cat', 'male')
let dog = _new(Animal, 'dog', 'male')
console.log(cat)
console.log(dog)
// 控制台输出
// {name: "cat", sex: "male"}
// Animal {type: "animal"}
修复
function _new(fn, ...args) {
let obj = new Object()
obj.__proto__ = fn.prototype
let ret = fn.apply(obj, args)
// 如果该函数没有返回对象,则返回this。
return typeof ret === 'object' ? ret : obj
}
// {name: "cat", sex: "male"}
// {name: "dog", sex: "male"}
JSON.stringify
Boolean | Number| String
类型会自动转换成对应的原始值undefined
、任意函数以及symbol
,会被忽略(出现在非数组对象的属性值中时),或者被转换成null
(出现在数组中时)- 不可枚举的属性会被忽略
- 如果一个对象的属性值通过某种间接的方式指回该对象本身,即循环引用,属性也会被忽略
function jsonStringify(obj) {
const type = typeof obj
if (type !== 'object' || obj === null) {
if (/string|undefined|function/.test(type)) {
obj = `"${obj}"`
}
return String(obj)
} else {
const json = []
const isArr = Array.isArray(obj)
for (let key in obj) {
let _value = obj[key]
let _type = typeof _value
if (/string|undefined|function/.test(_type)) {
_value = `"${_value}"`
} else if (_type === 'object') {
_value = jsonStringify(_value)
}
json.push(`${isArr ? '' : '"' + key + '":'}${_value.toString()}`)
}
return `${isArr ? '[' : '{'}${json.join(',')}${isArr ? ']' : '}'}`
}
}
console.log(JSON.stringify([1, 'false', false]))
console.log(jsonStringify([1, 'false', false]))
console.log(JSON.stringify(null))
console.log(jsonStringify(null))
JSON.parse
call
/ apply
手写 Function.call
按套路出牌
1.call
的核心:
- 将函数设为对象的属性
- 执行并删除这个函数
- 指定
this
到函数并传入给定阐述执行函数 - 如果不传入函数,默认指向为
window
JavaScript实现
实现(10).add(10).add(10)
链式调用
Number.prototype.add = function(num) {
if (
Object.prototype.toString.call(this) !== '[object Number]' ||
Object.prototype.toString.call(num) !== '[object Number]'
) {
throw new TypeError('类型不是 Number')
}
return this + num
}
let ret = (10).add(10).add(10) // 30
参考资料