# Function
# Call
- 第一个参数为null或者undefined时,this指向全局对象window,值为原始值的指向该原始值的自动包装对象,如 String、Number、Boolean 为了避免函数名与上下文(context)的属性发生冲突,使用Symbol类型作为唯一值
- 将函数作为传入的上下文(context)属性执行
- 函数执行完成后删除该属性
- 返回执行结果
Function.prototype.myCall = function(context, ...args) {
//处理未传ctx上下文,或者传的是null和undefined等场景
context = (context ?? window) || new Object(context)
// 用Symbol生成唯一的key
const key = Symbol()
// 这里的this,即要调用的函数
context[key] = this
// 将args展开,并且调用fnName函数,此时fnName函数内部的this也就是ctx了
const result = context[key](...args)
// 用完之后,将fnName从上下文ctx中删除
delete context[key]
return result
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# Apply
- 前部分与call一样
- 第二个参数可以不传,但类型必须为数组或者类数组
Function.prototype.myApply = function(context) {
context = (context ?? window) || new Object(context)
const key = Symbol()
const args = arguments[1]
context[key] = this
const result= args ? context[key](...args) : context[key]()
delete context[key]
return result
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# Bind
- 使用 call / apply 指定 this
- 返回一个绑定函数
- 当返回的绑定函数作为构造函数被new调用,绑定的上下文指向实例对象
- 设置绑定函数的prototype 为原函数的prototype
//简易版
Function.prototype.mybind = function (context, ...args) {
return (...newArgs) => {
return this.call(context, ...args, newArgs);
};
};
//完善版
Function.prototype.mybind2 = function (context, ...args) {
const fn = this;
const bindFn = function (...newFnArgs) {
return fn.call(
this instanceof bindFn ? this : context,
...args,
...newFnArgs
);
};
bindFn.prototype = Object.create(fn.prototype);
return bindFn;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# New
- 创建一个新对象
- 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象)
- 执行构造函数中的代码(为这个新对象添加属性)
- 返回新对象。
Function.prototype.myNew = function () {
//创建一个实例对象
var obj = new Object();
//取得外部传入得构造器
var Constructor = Array.prototype.shift.call(arguments);
//实现继承,实例可以访问构造器得属性
obj.__proto__ = Constructor.prototype;
//调用构造器,并改变其this 指向到实例
var ret = Constructor.apply(obj, arguments);
// 如果构造函数返回值是对象原则返回这个对象,如果不是对象则返回新的实例对象
return typeof ret === "object" ? ret : obj;
};
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# Object
# Object.create
- Object.create() 方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。
function create(obj) {
// 排除传入的对象是 null 和 非object的情况
if (obj === null || typeof obj !== 'object') {
throw new TypeError(`Object prototype may only be an Object: ${obj}`);
}
function Fn() {}
Fn.prototype = obj;
Fn.prototype.constructor = Fn;
return new Fn();
}
let demo = {
c: "123",
};
let cc = Object.create(demo);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Object.is
- Object.is不会转换被比较的两个值的类型,这点和===更为相似,他们之间也存在一些区别。
+0 === -0; // true
NaN === NaN; // false
1
2
2
- NaN在===中是不相等的,而在Object.is中是相等的
- +0和-0在===中是相等的,而在Object.is中是不相等的
Object.is = function (x, y) {
if (x === y) {
// 当前情况下,只有一种情况是特殊的,即 +0 -0
// 如果 x !== 0,则返回true
// 如果 x === 0,则需要判断+0和-0,则可以直接使用 1/+0 === Infinity 和 1/-0 === -Infinity来进行判断
return x !== 0 || 1 / x === 1 / y;
}
// x !== y 的情况下,只需要判断是否为NaN,如果x!==x,则说明x是NaN,同理y也一样
// x和y同时为NaN时,返回true
return x !== x && y !== y;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# Object.assign
- assign接收多个对象,并将多个对象合成一个对象
- 这些对象如果有重名属性,以后来的对象属性值为准
- assign返回一个对象,这个对象 === 第一个对象
Object.prototype.assign = function (target, ...args) {
if (target === null || target === undefined) {
throw new TypeError('Cannot convert undefined or null to object')
}
target = Object(target)
for (let nextObj of args) {
for (let key in nextObj) {
nextObj.hasOwnProperty(key) && (target[key] = nextObj[key])
}
}
return target
}
const testa = { name: 'testa' }
const testb = { name: 'testb', age: 12 }
const testc = { age: 28, gender: '男' }
const testd = Object.sx_assign(testa, testb, testc)
console.log(testd) // { name: 'testb', age: 28, gender: '男' }
console.log(testa === testd) // true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Object.freeze
Object.freeze冻结一个对象,让其不能再添加/删除属性,也不能修改该对象已有属性的可枚举性、可配置可写性
也不能修改已有属性的值和它的原型属性,最后返回一个和传入参数相同的对象
function myFreeze(obj){
// 判断参数是否为Object类型,如果是就封闭对象,循环遍历对象。去掉原型属性,将其writable特性设置为false
if(obj instanceof Object){
Object.seal(obj); // 封闭对象
for(let key in obj){
if(obj.hasOwnProperty(key)){
Object.defineProperty(obj,key,{
writable:false // 设置只读
})
// 如果属性值依然为对象,要通过递归来进行进一步的冻结
myFreeze(obj[key]);
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Object.entries
- 用处:将对象转成键值对数组
const obj = {
name: 'test123',
age: 22,
gender: '男'
}
1
2
3
4
5
2
3
4
5
Object.prototype.myEntries = function (obj) {
const res = []
for (let key in obj) {
obj.hasOwnProperty(key) && res.push([key, obj[key]])
}
return res
}
console.log(Object.myEntries(obj))
// [ [ 'name', 'test123' ], [ 'age', 22 ], [ 'gender', '男' ] ]
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# Object.fromEntries
- 跟entries相反,将键值对数组转成对象
Object.prototype.MyFromEntries = function (arr) {
const obj = {}
for (let i = 0; i < arr.length; i++) {
const [key, value] = arr[i]
obj[key] = value
}
return obj
}
console.log(Object.MyFromEntries([['name', 'test123'], ['age', 22], ['gender', '男']]))
// { name: 'test123', age: 22, gender: '男' }
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# Object.keys
- 将对象的key转成一个数组合集
Object.prototype.keys = function (obj) {
const keys = []
for (let key in obj) {
obj.hasOwnProperty(key) && res.push(key)
}
return keys
}
console.log(Object.keys(obj))
// [ 'name', 'age', 'gender' ]
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# Object.getOwnPropertyNames
- Object.getOwnPropertyNames返回的是对象所有自己的属性,而Object.keys(obj)则返回的是所有可枚举属性,也就是属性下的enumerable: false,看例子:
const obj = {};
Object.defineProperties(obj, {
property1: {enumerable: true, value: 1},
property2: {enumerable: false, value: 2},
});
console.log(Object.keys(obj));
console.log(Object.getOwnPropertyNames(obj));
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
- 实现
if (typeofObject.getOwnPropertyNames !== 'function') {
Object.getOwnPropertyNames = function(o) {
if (o !== Object(o)) {
throwTypeError('Object.getOwnPropertyNames called on non-object');
}
var props = [],
p;
for (p in o) {
if (Object.prototype.hasOwnProperty.call(o, p)) {
props.push(p);
}
}
return props;
};
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Object.values
- 将对象的所有值转成数组合集
Object.prototype.values = function (obj) {
const values = []
for (let key in obj) {
obj.hasOwnProperty(key) && values.push(obj[key])
}
return values
}
console.log(Object.values(obj))
// [ 'test123', 22, '男' ]
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# instanceOf
- 步骤1:先取得当前类的原型,当前实例对象的原型链
- 步骤2:一直循环(执行原型链的查找机制)
- 取得当前实例对象原型链的原型链(proto = proto.proto,沿着原型链一直向上查找)
- 如果 当前实例的原型链____proto__上找到了当前类的原型prototype,则返回 true
- 如果 一直找到Object.prototype.proto == null,Object的基类(null)上面都没找到,则返回 false
function _instanceof (instanceObject, classFunc) {
let classFunc = classFunc.prototype; // 取得当前类的原型
let proto = instanceObject.__proto__; // 取得当前实例对象的原型链
while (true) {
if (proto === null) { // 找到了 Object的基类 Object.prototype.__proto__
return false;
};
if (proto === classFunc) { // 在当前实例对象的原型链上,找到了当前类
return true;
}
proto = proto.__proto__; // 沿着原型链__ptoto__一层一层向上查找
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
- 优化版 (处理兼容问题)
Object.getPrototypeOf ():用来获取某个实例对象的原型(内部[[prototype]]属性的值,包含proto属性)
const _instanceof = (target, Fn) => {
// 补全代码
//基本数据类型的判断
if(target === null || typeof target !== 'object'){
return false
}
//获取第一个原型
let leftProto = Object.getPrototypeOf(target)
while(true){
if (leftProto === null){
return false
}
//和第二个原型进行比较
if(leftProto == Fn.prototype){
return true
}
leftProto = Object.getPrototypeOf(leftProto)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 自定义typeOf
typeof 可以正确识别:Undefined、Boolean、Number、String、Symbol、Function 等类型的数据,但是对于其他的都会认为是 object,比如 Null、Date 等,所以通过 typeof 来判断数据类型会不准确。但是可以使用 Object.prototype.toString 实现。
function typeOf(obj) {
let res = Object.prototype.toString.call(obj).split(' ')[1]
res = res.substring(0, res.length - 1).toLowerCase()
return res
}
typeOf([]) // 'array'
typeOf({}) // 'object'
typeOf(new Date) // 'date'
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# JSON.stringify
- JSON.stringify([, replacer [, space]) 方法是将一个 JavaScript 值(对象或者数组)转换为一个 JSON 字符串。此处模拟实现,不考虑可选的第二个参数 replacer 和第三个参数 space,如果对这两个参数的作用还不了解,建议阅读 MDN 文档。
- 基本数据类型:
- undefined 转换之后仍是 undefined(类型也是 undefined)
- boolean 值转换之后是字符串 "false"/"true"
- number 类型(除了 NaN 和 Infinity)转换之后是字符串类型的数值
- symbol 转换之后是 undefined
- null 转换之后是字符串 "null"
- string 转换之后仍是string
- NaN 和 Infinity 转换之后是字符串 "null"
- 基本数据类型:
函数类型:转换之后是 undefined
如果是对象类型(非函数)
- 如果是一个数组:如果属性值中出现了 undefined、任意的函数以及 symbol,转换成字符串 "null"
- 如果是 RegExp 对象:返回 {} (类型是 string);
- 如果是 Date 对象,返回 Date 的 toJSON 字符串值;
- 如果是普通对象;
- 如果有 toJSON() 方法,那么序列化 toJSON() 的返回值。
- 如果属性值中出现了 undefined、任意的函数以及 symbol 值,忽略。
- 所有以 symbol 为属性键的属性都会被完全忽略掉。
对包含循环引用的对象(对象之间相互引用,形成无限循环)执行此方法,会抛出错误。
function jsonStringify(data) {
let dataType = typeof data;
if (dataType !== 'object') {
let result = data;
//data 可能是 string/number/null/undefined/boolean
if (Number.isNaN(data) || data === Infinity) {
//NaN 和 Infinity 序列化返回 "null"
result = "null";
} else if (dataType === 'function' || dataType === 'undefined' || dataType === 'symbol') {
//function 、undefined 、symbol 序列化返回 undefined
return undefined;
} else if (dataType === 'string') {
result = '"' + data + '"';
}
//boolean 返回 String()
return String(result);
} else if (dataType === 'object') {
if (data === null) {
return "null"
} else if (data.toJSON && typeof data.toJSON === 'function') {
return jsonStringify(data.toJSON());
} else if (data instanceof Array) {
let result = [];
//如果是数组
//toJSON 方法可以存在于原型链中
data.forEach((item, index) => {
if (typeof item === 'undefined' || typeof item === 'function' || typeof item === 'symbol') {
result[index] = "null";
} else {
result[index] = jsonStringify(item);
}
});
result = "[" + result + "]";
return result.replace(/'/g, '"');
} else {
//普通对象
/**
* 循环引用抛错(暂未检测,循环引用时,堆栈溢出)
* symbol key 忽略
* undefined、函数、symbol 为属性值,被忽略
*/
let result = [];
Object.keys(data).forEach((item, index) => {
if (typeof item !== 'symbol') {
//key 如果是symbol对象,忽略
if (data[item] !== undefined && typeof data[item] !== 'function'
&& typeof data[item] !== 'symbol') {
//键值如果是 undefined、函数、symbol 为属性值,忽略
result.push('"' + item + '"' + ":" + jsonStringify(data[item]));
}
}
});
return ("{" + result + "}").replace(/'/g, '"');
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# JSON.parse
- eval
var json = '{"a":"1", "b":2}';
var obj = eval("(" + json + ")"); // obj 就是 json 反序列化之后得到的对象
1
2
3
2
3
- 但是直接调用 eval 会存在安全问题,如果数据中可能不是 json 数据,而是可执行的 JavaScript 代码,那很可能会造成 XSS 攻击。因此,在调用 eval 之前,需要对数据进行校验。
var rx_one = /^[\],:{}\s]*$/;
var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;
var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
var rx_four = /(?:^|:|,)(?:\s*\[)+/g;
if (
rx_one.test(
json.replace(rx_two, "@")
.replace(rx_three, "]")
.replace(rx_four, "")
)
) {
var obj = eval("(" +json + ")");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- new Function
var json = '{"name":"小姐姐", "age":20}';
var obj = (new Function('return ' + json))();
1
2
3
2
3
- 参考
JSON.parse 三种实现方式 (opens new window)
# 手写实现一个迭代器(Iterator)
ES6迭代器具有如下特征:
- 迭代器返回一个对象;
- 该对象有一个 next() 方法;
- 调用该方法会返回一个对象;
- 该对象包含两个属性,value 和 done
- value 表示返回的值,遍历完之后一直返回 undefined;
- done 表示是否遍历结束。
let myIterator = (arr) => {
let index = 0
return {
next: () => {
return {
value: arr[index++] || undefined,
done: index > arr.length
}
}
}
}
// test
let arr = [1, 4, 'ads']
let iteratorObj = myIterator(arr)
console.log(iteratorObj.next()) // { value: 1, done: false }
console.log(iteratorObj.next()) // { value: 4, done: false }
console.log(iteratorObj.next()) // { value: 'ads', done: false }
console.log(iteratorObj.next()) // { value: undefined, done: true }
console.log(iteratorObj.next()) // { value: undefined, done: true }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# String
# slice
start:开始截取的字符索引(包含此字符)
end:结束截取的字符索引(不包含此字符)
注意点:
- start > end:返回空字符串
- start < 0:start = 数组长度 + start
String.prototype.sx_slice = function (start = 0, end) {
start = start < 0 ? this.length + start : start
end = !end && end !== 0 ? this.length : end
if (start >= end) return ''
let str = ''
for (let i = start; i < end; i++) {
str += this[i]
}
return str
}
console.log(str.sx_slice(2)) // nshine_lin
console.log(str.sx_slice(-2)) // in
console.log(str.sx_slice(-9, 10)) // shine_l
console.log(str.sx_slice(5, 1)) // ''
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# substr
start:开始截取的字符索引(包含此字符)
length:截取的长度
注意点
- start < 0:start = 数组长度 + start
- length超出所能截取范围,需要做处理
- length < 0:返回空字符串
String.prototype.sx_substr = function (start = 0, length) {
if (length < 0) return ''
start = start < 0 ? this.length + start : start
length = (!length && length !== 0) || length > this.length - start ? this.length : start + length
let str = ''
for (let i = start; i < length; i++) {
str += this[i]
}
return str
}
console.log(str.sx_substr(3)) // shine_lin
console.log(str.sx_substr(3, 3)) // shi
console.log(str.sx_substr(5, 300)) // ine_lin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# substring
- 功能与slice大致相同,start > end:互换值
String.prototype.sx_sunstring = function (start = 0, end) {
start = start < 0 ? this.length + start : start
end = !end && end !== 0 ? this.length : end
if (start >= end) [start, end] = [end, start]
let str = ''
for (let i = start; i < end; i++) {
str += this[i]
}
return str
}
console.log(str.sx_sunstring(2)) // nshine_lin
console.log(str.sx_sunstring(-2)) // in
console.log(str.sx_sunstring(-9, 10)) // shine_l
console.log(str.sx_sunstring(5, 1)) // unsh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 将一个字符串转换为驼峰形式
//方式一:操作字符串数组
function transformStr2Hump1(str) {
if(str == null) {
return "";
}
var strArr = str.split('-');
for(var i = 1; i < strArr.length; i++) {
strArr[i] = strArr[i].charAt(0).toUpperCase() + strArr[i].substring(1);
}
return strArr.join('');
}
//方式二:操作字符数组
function transformStr2Hump2(str) {
if(str == null) {
return "";
}
var strArr =str.split('');
for(var i = 0; i < strArr.length; i++) {
if(strArr[i] == "-"){
//删除-
strArr.splice(i, 1);
//将该处改为大写
if(i < strArr.length) {
strArr[i] = strArr[i].toUpperCase();
}
}
}
return strArr.join("");
}
//方式三:利用正则
function transformStr2Hump3(str) {
if(str == null) {
return "";
}
var reg = /-(\w)/g;//匹配字母或数字或下划线或汉字
return str.replace(reg, function($0, $1) {
return $1.toUpperCase();
})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# camelCase和snake_case互转
- snake_case转换为camelCase
/**
* @param {string} str
* @return {string}
*/
function snakeToCamel(str) {
const reg = /([^_])_([^_])/g
return str.replaceAll(reg, (_, p1, p2) => `${p1}${p2.toUpperCase()}`)
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
- camelCase转换为snake_case
/*
* @param {string} str
* @return {string}
*/
function camelToSnake(str) {
const reg = /\B(\w)([A-Z])\B/g
return str.replaceAll(reg, (_, p1, p2) => `${p1}_${p2.toLowerCase()}`)
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# ES6函数
# Set
class Set {
constructor() {
this.items = {};
this.size = 0;
}
has(element) {
return element in this.items;
}
add(element) {
if(! this.has(element)) {
this.items[element] = element;
this.size++;
}
return this;
}
delete(element) {
if (this.has(element)) {
delete this.items[element];
this.size--;
}
return this;
}
clear() {
this.items = {}
this.size = 0;
}
values() {
let values = [];
for(let key in this.items) {
if(this.items.hasOwnProperty(key)) {
values.push(key);
}
}
return values;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# Map
function defaultToString(key) {
if(key === null) {
return 'NULL';
} else if (key === undefined) {
return 'UNDEFINED'
} else if (Object.prototype.toString.call(key) === '[object Object]' || Object.prototype.toString.call(key) === '[object Array]') {
return JSON.stringify(key);
}
return key.toString();
}
class Map {
constructor() {
this.items = {};
this.size = 0;
}
set(key, value) {
if(!this.has(key)) {
this.items[defaultToString(key)] = value;
this.size++;
}
return this;
}
get(key) {
return this.items[defaultToString(key)];
}
has(key) {
return this.items[defaultToString(key)] !== undefined;
}
delete(key) {
if (this.has(key)) {
delete this.items[key];
this.size--;
}
return this;
}
clear() {
this.items = {}
this.size = 0;
}
keys() {
let keys = [];
for(let key in this.items) {
if(this.has(key)) {
keys.push(key)
}
}
return keys;
}
values() {
let values = [];
for(let key in this.items) {
if(this.has(key)) {
values.push(this.items[key]);
}
}
return values;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# Class
- ES6
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
eat() {
return 'eat'
}
static say() {
return 'say'
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
- ES5
// 判断某对象是否为某构造器的实例
function _instanceof(left, right) {
if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
return !!right[Symbol.hasInstance](left); } else { return left instanceof right; }
}
// 检查声明的class类是否通过new的方式调用,否则会报错
function _classCallCheck(instance, Constructor) { if (!_instanceof(instance, Constructor)) {
throw new TypeError("Cannot call a class as a function"); }
}
/**
*将方法添加到原型上,如果是静态方法添加到构造函数上,
**/
function _defineProperties(target, props) {
// 遍历函数数组,分别声明其描述符 并添加到对应的对象上
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false; // 设置该属性是否能够出现在对象的枚举属性中。默认为 false
descriptor.configurable = true; // 设置该属性描述符能够被改变,同时该属性也能从对应的对象上被删除。
if ("value" in descriptor) descriptor.writable = true; // 如果属性中存在value, value设置为可以改变。
Object.defineProperty(target, descriptor.key, descriptor); // 写入对应的对象上
}
}
// 收集公有函数和静态方法,将方法添加到构造函数或构造函数的原型中,并返回构造函数。
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps); // 共有方法写在property原型上
if (staticProps) _defineProperties(Constructor, staticProps); // 静态方法写到构造函数上
return Constructor;
}
var Person = function () {
function Person(name, age) {
_classCallCheck(this, Person);
this.name = name;
this.age = age;
}
_createClass(Person, [{
key: "eat",
value: function eat() {
return 'eat';
}
}], [{
key: "say",
value: function say() {
return 'say';
}
}]);
return Person;
}();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57