Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What are the inheritance types of JS

2025-03-31 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article introduces the relevant knowledge of "what are the inheritance types of JS". In the operation of actual cases, many people will encounter such a dilemma, so let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!

Data type judgment

Typeof can correctly identify: Undefined, Boolean, Number, String, Symbol, Function and other types of data, but for other types of data will be considered as object, such as Null, Date, etc., so it is not accurate to judge the data type by typeof. However, it can be implemented using 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' inheritance prototype chain inherits function Animal () {this.colors = [' black' 'white']} Animal.prototype.getColor = function () {return this.colors} function Dog () {} Dog.prototype = new Animal () let dog1 = new Dog () dog1.colors.push (' brown') let dog2 = new Dog () console.log (dog2.colors) / / ['black',' white', 'brown']

Problems with prototype chain inheritance:

Question 1: the reference type properties contained in the prototype will be shared by all instances

Problem 2: subclasses cannot pass parameters to the parent class constructor when instantiating

Borrow the constructor to inherit function Animal (name) {this.name = name this.getName = function () {return this.name}} function Dog (name) {Animal.call (this, name)} Dog.prototype = new Animal ()

Using constructor to realize inheritance solves two problems of prototype chain inheritance: reference type sharing and parameter passing. However, because the method must be defined in the constructor, it causes the method to be created every time an instance of the subclass is created.

Combinatorial inheritance

Combinatorial inheritance combines prototype chains and embezzlement constructors, bringing together the advantages of both. The basic idea is to inherit the properties and methods on the prototype using the prototype chain and to inherit the instance properties through embezzlement of the constructor. This not only defines the method on the prototype for reuse, but also allows each instance to have its own properties.

Function Animal (name) {this.name = name this.colors = ['black',' white']} Animal.prototype.getName = function () {return this.name} function Dog (name, age) {Animal.call (this, name) this.age = age} Dog.prototype = new Animal () Dog.prototype.constructor = Dog let dog1 = new Dog ('milkshake', 2) dog1.colors.push ('brown') let dog2 = new Dog (' Hachi') 1) console.log (dog2) / / {name: "Hatch", colors: ["black", "white"], age: 1} parasitic combination inheritance

Combinatorial inheritance is relatively perfect, but there is still a problem. The problem is that the parent constructor is called twice, the first in new Animal () and the second in Animal.call ().

So the solution is not to directly call the parent class constructor to assign values to the subclass prototype, but to get a copy of the parent prototype by creating an empty function F.

Parasitic combinatorial inheritance is basically similar to combinatorial inheritance, but the difference is as follows:

-Dog.prototype = new Animal ()-Dog.prototype.constructor = Dog + function F () {} + F.prototype = Animal.prototype + let f = new F () + f.constructor = Dog + Dog.prototype = f

After slightly encapsulating the code added above:

Function object (o) {function F () {} F.prototype = o return new F ()} function inheritPrototype (child, parent) {let prototype = object (parent.prototype) prototype.constructor = child child.prototype = prototype} inheritPrototype (Dog, Animal)

If you dislike too much of the above code, you can also change it to the simplest parasitic combinatorial inheritance based on combinatorial inheritance:

-Dog.prototype = new Animal ()-Dog.prototype.constructor = Dog + Dog.prototype = Object.create (Animal.prototype) + Dog.prototype.constructor = Dog

Class implementation inheritance

Class Animal {constructor (name) {this.name = name} getName () {return this.name}} class Dog extends Animal {constructor (name, age) {super (name) this.age = age}} array

ES5 implementation:

Function unique (arr) {var res = arr.filter (function (item, index, array) {return array.indexOf (item) = = index}) return res}

ES6 implementation:

Var unique = arr = > [... new Set (arr)] array flattening

Array flattening is to flatten the multi-layered array [1, [2, [3]] into one layer [1, 2, 3]. Using Array.prototype.flat, you can directly flatten a multi-layer array into one layer:

[1, [2, [3]] .flat (2) / / [1,2,3]

Now is to achieve this effect of flat.

ES5 implementation: recursion.

Function flatten (arr) {var result = []; for (var I = 0, len = arr.length; I)

< len; i++) { if (Array.isArray(arr[i])) { result = result.concat(flatten(arr[i])) } else { result.push(arr[i]) } } return result; } ES6 实现: function flatten(arr) { while (arr.some(item =>

Array.isArray (item)) {arr = [] .concat (.arr);} return arr;} Deep and shallow copy

Shallow copy: only object types are considered.

Function shallowCopy (obj) {if (typeof obj! = = 'object') return let newObj = obj instanceof Array? []: {} for (let key in obj) {if (obj.hasOwnProperty (key)) {newObj [key] = obj [key]}} return newObj}

Simple deep copy: only ordinary object properties are considered, not built-in objects and functions.

Function deepClone (obj) {if (typeof obj! = = 'object') return; var newObj = obj instanceof Array? []: {}; for (var key in obj) {if (obj.hasOwnProperty (key)) {newObj [key] = typeof obj [key] = =' object'? DeepClone (obj [key]): obj [key];}} return newObj;}

Complex version of deep cloning: based on the simple version, it also considers built-in objects such as Date, RegExp and other objects and functions and solves the problem of circular reference.

Const isObject = (target) = > (typeof target = "object" | | typeof target = "function") & & target! = null; function deepClone (target, map = new WeakMap ()) {if (map.get (target)) {return target;} / / the constructor that gets the current value: gets its type let constructor = target.constructor / / check whether the current object target matches the regular, date format object if (/ ^ (RegExp | Date) $/ i.test (constructor.name)) {/ / create an instance of a new special object (regular class / date class) return new constructor (target);} if (isObject (target)) {map.set (target, true) / / Mark circular referenced objects const cloneTarget = Array.isArray (target)? []: {}; for (let prop in target) {if (target.hasOwnProperty (prop)) {cloneTarget [prop] = deepClone (target [prop], map);} return cloneTarget;} else {return target }} event bus (publish / subscribe mode) class EventEmitter {constructor () {this.cache = {}} on (name, fn) {if (this.cache [name]) {this.cache [name] .push (fn)} else {this.cache [name] = [fn]}} off (name Fn) {let tasks = this.cache [name] if (tasks) {const index = tasks.findIndex (f = > f = fn | | f.callback = fn) if (index > = 0) {tasks.splice (index, 1)} emit (name, once = false ... args) {if (this.cache [name]) {/ / create a copy If the same event continues to be registered within the callback function Will cause an endless loop of let tasks = this.cache.slice () for (let fn of tasks) {fn (... args)} if (once) {delete this.cache [name]} / / Test let eventBus = new EventEmitter () let fn1 = function (name) Age) {console.log (`${name} ${age}`)} let fn2 = function (name, age) {console.log (`hello, ${name} ${age} `)} eventBus.on ('aaa', fn1) eventBus.on (' aaa', fn2) eventBus.emit ('aaa', false,' Bran', 12) / / 'Bran 12' / /' hello Bran parses the URL parameter to the object function parseParam (url) {const paramsStr = /. +\? (. +) $/ .exec (url) [1] / / will? The following string takes out const paramsArr = paramsStr.split ('&'); / / splits the string with & and stores it in the array let paramsObj = {}; / / saves params in the object paramsArr.forEach (param = > {if (/ = / .test (param)) {/ / processes the parameter let [key, val] = param.split ('=') with value / / split key and valueval = decodeURIComponent (val); / / decode val = / ^\ d+$/.test (val)? ParseFloat (val): val; / / determines whether to convert to digital if (paramsObj.hasOwnProperty (key)) {/ / if the object has key, add a value paramsObj [key] = [] .concat (paramsObj [key], val);} else {/ / if the object does not have this key, create key and set the value paramsObj [key] = val }} else {/ / processing parameters without value paramsObj [param] = true;}}) return paramsObj;} string template function render (template, data) {const reg = /\ {(\ w+)\}\} / / / template string regular if (reg.test (template)) {/ / determine whether there is a template string const name = reg.exec (template) [1] in the template; / / find the field template = template.replace (reg, data [name]) of the first template string in the current template; / / render the first template string return render (template, data) / / render recursively and return the rendered structure} return template; / / if the template does not have a template string, return directly}

Test:

Let template ='I am {{name}}, age {{age}}, gender {{sex}}'; let person = {name: 'Bran', age: 12} render (template, person); / / I am Bran, age 12, sex undefined pictures are loaded lazily

Unlike ordinary picture lazy loading, the following two more elaborate treatments have been done:

Remove event listeners after all images have been loaded

The loaded picture is removed from imgList.

Let imgList = [... document.querySelectorAll ('img')] let length = imgList.length const imgLazyLoad = function () {let count = 0 return function () {let deleteIndexList = [] imgList.forEach ((img, index) = > {let rect = img.getBoundingClientRect () if (rect.top)

< window.innerHeight) { img.src = img.dataset.src deleteIndexList.push(index) count++ if (count === length) { document.removeEventListener('scroll', imgLazyLoad) } } }) imgList = imgList.filter((img, index) =>

! deleteIndexList.includes (index)}} / / it's better to add anti-shake processing document.addEventListener ('scroll', imgLazyLoad) function to anti-shake.

The high-frequency event will be executed only once after N seconds of triggering, and if the event is triggered again within N seconds, it will be rescheduled.

Simple version: this and event objects are supported inside the function

Function debounce (func, wait) {var timeout; return function () {var context = this; var args = arguments; clearTimeout (timeout) timeout = setTimeout (function () {func.apply (context, args)}, wait);}

Use:

Var node = document.getElementById ('layout') function getUserAction (e) {console.log (this, e) / / print: node this node and MouseEvent Node [XSS _ clean] = count++;}; node.onmousemove = debounce (getUserAction, 1000)

Final version: in addition to supporting this and event, it also supports the following features:

Support immediate execution

Function may have a return value

Support for cancellation function

Function debounce (func, wait, immediate) {var timeout, result; var debounced = function () {var context = this; var args = arguments; if (timeout) clearTimeout (timeout); if (immediate) {/ / if it has been executed, var callNow =! timeout is no longer executed Timeout = setTimeout (function () {timeout = null;}, wait) if (callNow) result = func.apply (context, args)} else {timeout = setTimeout (function () {func.apply (context, args)}, wait);} return result;} Debounced.cancel = function () {clearTimeout (timeout); timeout = null;}; return debounced;}

Use:

Var setUseAction = debounce (getUserAction, 10000, true); / / use anti-shake node.onmousemove = setUseAction / / cancel anti-shake setUseAction.cancel ()

Reference: JavaScript project to learn anti-shaking from underscore

Function throttling

High-frequency events are triggered and executed only once in N seconds.

Simple version: use a timestamp to implement it, execute it immediately, and then execute it every N seconds.

Function throttle (func, wait) {var context, args; var previous = 0; return function () {var now = + new Date (); context = this; args = arguments; if (now-previous > wait) {func.apply (context, args); previous = now;}

Final version: unthrottling is supported; in addition, the third parameter, options.leading, is passed to indicate whether it can be executed immediately, and opitons.trailing indicates whether to execute it again at the end of the call. The default is true. Note that you cannot set leading or trailing to false at the same time.

Function throttle (func, wait, options) {var timeout, context, args, result; var previous = 0; if (! options) options = {}; var later = function () {previous = options.leading = = false? 0: new Date (). GetTime (); timeout = null; func.apply (context, args); if (! timeout) context = args = null;} Var throttled = function () {var now = new Date (). GetTime (); if (! previous & & options.leading = false) previous = now; var remaining = wait-(now-previous); context = this; args = arguments; if (remaining wait) {if (timeout) {clearTimeout (timeout); timeout = null } previous = now; func.apply (context, args); if (! timeout) context = args = null;} else if (! timeout & & options.trailing! = = false) {timeout = setTimeout (later, remaining);}}; throttled.cancel = function () {clearTimeout (timeout); previous = 0 Timeout = null;} return throttled;}

The use of throttling does not take the code as an example, just refer to the anti-shake writing line.

Corialization of function

What is the function Corialization? In fact, it is the technology of converting functions that use multiple parameters into a series of functions that use one parameter. Don't you get it? Let's give an example.

Function add (a, b, c) {return a + b + c} add (1,2,3) let addCurry = curry (add) addCurry (1) (2) (3)

Now it's time to implement the curry function, which changes the function from passing in multiple parameters in one call to passing one parameter in each call.

Function curry (fn) {let judge = (... args) = > {if (args.length = = fn.length) return fn (... args) return (... arg) = > judge (... args,... arg)} return judge} partial function

What is a partial function? A partial function is to convert a function with n parameters into a function with fixed x parameters, and all the remaining parameters (n-x) will be passed in the next call. For example:

Function add (a, b, c) {return a + b + c} let partialAdd = partial (add, 1) partialAdd (2,3)

Found that no, in fact, partial function and function Coriolization are a bit like, so according to the implementation of function Corialization, we can quickly write the implementation of partial function:

Function partial (fn,... args) {return (... arg) = > {return fn (... args,... arg)}}

As mentioned above, this function is relatively simple, and now we hope that the partial function can achieve the same placeholder function as Coriarization, such as:

Function clg (a, b, c) {console.log (a, b, c)} let partialClg = partial (clg,'_', 2) partialClg (1, 3) / print: 1, 2, 3

The position occupied by _ is actually the position of 1. Equivalent to: partial (clg, 1, 2), then partialClg (3). Now that we understand the principle, let's write about the implementation:

Function partial (fn,... args) {return (... arg) = > {args [index] = return fn (... args,... arg)}} JSONP

JSONP core principle: script tags are not bound by the same origin policy, so they can be used for cross-domain requests. The advantage is good compatibility, but can only be used for GET requests.

Const jsonp = ({url, params) CallbackName}) = > {const generateUrl = () = > {let dataSrc =''for (let key in params) {if (params.hasOwnProperty (key)) {dataSrc + = `${key} = ${params [key]} &`}} dataSrc + = `callback=$ {callbackName} `return `${url}? ${dataSrc}`} return new Promise ((resolve) Reject) = > {const scriptEle = document.createElement ('script') scriptEle.src = generateUrl () document.body.appendChild (scriptEle) window [callbackName] = data = > {resolve (data) document.removeChild (scriptEle)}})} AJAXconst getJSON = function (url) {return new Promise ((resolve, reject) = > {const xhr = XMLHttpRequest? New XMLHttpRequest (): new ActiveXObject ('Mscrosoft.XMLHttp'); xhr.open (' GET', url, false); xhr.setRequestHeader ('Accept',' application/json'); xhr.onreadystatechange = function () {if (xhr.readyState! = = 4) return; if (xhr.status = 200 | | xhr.status = = 304) {resolve (xhr.responseText) } else {reject (new Error (xhr.responseText));}} xhr.send ();})} implement array prototype method

ForEach

Array.prototype.forEach3 = function (callback, thisArg) {if (this = = null) {throw new TypeError ('this is null or not defined')} if (typeof callback! = = "function") {throw new TypeError (callback +' is not a function')} const O = Object (this) / / this is the current array const len = O.length > 0 / / followed by an explanation let k = 0 while (k

< len) { if (k in O) { callback.call(thisArg, O[k], k, O); } k++; } } O.length >

What is > > 0? It means to move 0 bits to the right unsigned. What's the point? Is to ensure that the converted value is a positive integer. In fact, the bottom layer does two levels of conversion, the first is to convert non-number to number type, and the second is to convert number to Uint32 type. What does it mean to read something > > 0 if you are interested? [3].

Map

ForEach-based implementations can easily write map implementations:

-Array.prototype.forEach3 = function (callback, thisArg) {+ Array.prototype.map2 = function (callback, thisArg) {if (this = = null) {throw new TypeError ('this is null or not defined')} if (typeof callback! = = "function") {throw new TypeError (callback +' is not a function')} const O = Object (this) const len = O.length > > 0-let k = 0 + let k = 0 Res = [] while (k

< len) { if (k in O) { - callback.call(thisArg, O[k], k, O); + res[k] = callback.call(thisArg, O[k], k, O); } k++; } + return res }filter 同样,基于 forEach 的实现能够很容易写出 filter 的实现: - Array.prototype.forEach3 = function(callback, thisArg) { + Array.prototype.filter2 = function(callback, thisArg) { if (this == null) { throw new TypeError('this is null or not defined') } if (typeof callback !== "function") { throw new TypeError(callback + ' is not a function') } const O = Object(this) const len = O.length >

> 0-let k = 0 + let k = 0, res = [] while (k

< len) { if (k in O) { - callback.call(thisArg, O[k], k, O); + if (callback.call(thisArg, O[k], k, O)) { + res.push(O[k]) + } } k++; } + return res }some 同样,基于 forEach 的实现能够很容易写出 some 的实现: - Array.prototype.forEach3 = function(callback, thisArg) { + Array.prototype.some2 = function(callback, thisArg) { if (this == null) { throw new TypeError('this is null or not defined') } if (typeof callback !== "function") { throw new TypeError(callback + ' is not a function') } const O = Object(this) const len = O.length >

> > 0 let k = 0 while (k

< len) { if (k in O) { - callback.call(thisArg, O[k], k, O); + if (callback.call(thisArg, O[k], k, O)) { + return true + } } k++; } + return false }reduceArray.prototype.reduce2 = function(callback, initialValue) { if (this == null) { throw new TypeError('this is null or not defined') } if (typeof callback !== "function") { throw new TypeError(callback + ' is not a function') } const O = Object(this) const len = O.length >

> 0 let k = 0, acc if (arguments.length > 1) {acc = initialValue} else {/ / if no initial value is passed, the first non-empty value in the array is taken as the initial value while (k

< len && !(k in O)) { k++ } if (k >

Len) {throw new TypeError ('Reduce of empty array with no initial value');} acc = O [knot +]} while (k

< len) { if (k in O) { acc = callback(acc, O[k], k, O) } k++ } return acc }实现函数原型方法call 使用一个指定的 this 值和一个或多个参数来调用一个函数。 实现要点: this 可能传入 null; 传入不固定个数的参数; 函数可能有返回值; Function.prototype.call2 = function (context) { var context = context || window; context.fn = this; var args = []; for(var i = 1, len = arguments.length; i < len; i++) { args.push('arguments[' + i + ']'); } var result = eval('context.fn(' + args +')'); delete context.fn return result; }apply apply 和 call 一样,唯一的区别就是 call 是传入不固定个数的参数,而 apply 是传入一个数组。 实现要点: this 可能传入 null; 传入一个数组; 函数可能有返回值; Function.prototype.apply2 = function (context, arr) { var context = context || window; context.fn = this; var result; if (!arr) { result = context.fn(); } else { var args = []; for (var i = 0, len = arr.length; i < len; i++) { args.push('arr[' + i + ']'); } result = eval('context.fn(' + args + ')') } delete context.fn return result; }bind bind 方法会创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。 实现要点: bind() 除了 this 外,还可传入多个参数; bing 创建的新函数可能传入多个参数; 新函数可能被当做构造函数调用; 函数可能有返回值; Function.prototype.bind2 = function (context) { var self = this; var args = Array.prototype.slice.call(arguments, 1); var fNOP = function () {}; var fBound = function () { var bindArgs = Array.prototype.slice.call(arguments); return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs)); } fNOP.prototype = this.prototype; fBound.prototype = new fNOP(); return fBound; }实现 new 关键字 new 运算符用来创建用户自定义的对象类型的实例或者具有构造函数的内置对象的实例。 实现要点: new 会产生一个新对象; 新对象需要能够访问到构造函数的属性,所以需要重新指定它的原型; 构造函数可能会显示返回; function objectFactory() { var obj = new Object() Constructor = [].shift.call(arguments); obj.__proto__ = Constructor.prototype; var ret = Constructor.apply(obj, arguments); // ret || obj 这里这么写考虑了构造函数显示返回 null 的情况 return typeof ret === 'object' ? ret || obj : obj; }; 使用: function person(name, age) { this.name = name this.age = age } let p = objectFactory(person, '布兰', 12) console.log(p) // { name: '布兰', age: 12 }实现 instanceof 关键字 instanceof 就是判断构造函数的 prototype 属性是否出现在实例的原型链上。 function instanceOf(left, right) { let proto = left.__proto__ while (true) { if (proto === null) return false if (proto === right.prototype) { return true } proto = proto.__proto__ } } 上面的 left.proto 这种写法可以换成 Object.getPrototypeOf(left)。 实现 Object.create Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。 Object.create2 = function(proto, propertyObject = undefined) { if (typeof proto !== 'object' && typeof proto !== 'function') { throw new TypeError('Object prototype may only be an Object or null.') if (propertyObject == null) { new TypeError('Cannot convert undefined or null to object') } function F() {} F.prototype = proto const obj = new F() if (propertyObject != undefined) { Object.defineProperties(obj, propertyObject) } if (proto === null) { // 创建一个没有原型对象的对象,Object.create(null) obj.__proto__ = null } return obj }实现 Object.assignObject.assign2 = function(target, ...source) { if (target == null) { throw new TypeError('Cannot convert undefined or null to object') } let ret = Object(target) source.forEach(function(obj) { if (obj != null) { for (let key in obj) { if (obj.hasOwnProperty(key)) { ret[key] = obj[key] } } } }) return ret }实现 JSON.stringify JSON.stringify([, replacer [, space]) 方法是将一个 JavaScript 值(对象或者数组)转换为一个 JSON 字符串。此处模拟实现,不考虑可选的第二个参数 replacer 和第三个参数 space,如果对这两个参数的作用还不了解,建议阅读 MDN[4] 文档。 1.基本数据类型: undefined 转换之后仍是 undefined(类型也是 undefined) boolean 值转换之后是字符串 "false"/"true" number 类型(除了 NaN 和 Infinity)转换之后是字符串类型的数值 symbol 转换之后是 undefined null 转换之后是字符串 "null" string 转换之后仍是string NaN 和 Infinity 转换之后是字符串 "null" 2.函数类型:转换之后是 undefined 3.如果是对象类型(非函数) 如果有 toJSON() 方法,那么序列化 toJSON() 的返回值。 如果属性值中出现了 undefined、任意的函数以及 symbol 值,忽略。 所有以 symbol 为属性键的属性都会被完全忽略掉。 如果是一个数组:如果属性值中出现了 undefined、任意的函数以及 symbol,转换成字符串 "null" ; 如果是 RegExp 对象:返回 {} (类型是 string); 如果是 Date 对象,返回 Date 的 toJSON 字符串值; 如果是普通对象; 4.对包含循环引用的对象(对象之间相互引用,形成无限循环)执行此方法,会抛出错误。 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 {/ / ordinary object / * Circular reference throw error (not detected yet, stack overflow in case of circular reference) * symbol key ignores * undefined, function, symbol as attribute values, and is ignored * / let result = [] Object.keys (data) .forEach ((item, index) = > {if (typeof item! = = 'symbol') {/ / key if it is a symbol object Ignore if (data [item]! = = undefined & & typeof data [item]! = 'function' & & typeof data [item]!! =' symbol') {/ / key value if undefined, function, symbol are attribute values Ignore result.push (''+ item +''+ ":" + jsonStringify (data [item])) ); return ("{" + result + "}") .replace (/'/ g,'");} implement JSON.parse

This paper introduces two methods to implement:

Eval implementation

New Function implementation

Eval implementation

The first method is the simplest and most intuitive, which is to call eval directly. The code is as follows:

Var json ='{"a": "1", "b": 2}'; var obj = eval ("(" + json + ")"); / / obj is the object obtained after json deserialization

However, calling eval directly can have security problems, and if the data may not be json data, but executable JavaScript code, it is likely to cause XSS attacks. Therefore, the data needs to be validated before calling 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 +") ");} new Function implementation

Function has the same string parameter properties as eval.

Var json ='{"name": "Little Sister", "age": 20}'; var obj = (new Function ('return' + json)) (); implement Promise implementation

Promise needs to fully understand the Promise A+ specification [7], but in terms of overall implementation, there are several points to consider:

Then needs to support chained calls, so you have to return a new Promise

Deal with asynchronous problems, so you have to use onResolvedCallbacks and onRejectedCallbacks to store successful and failed callbacks respectively

For chained calls to work properly, you need to determine the type of onFulfilled and onRejected

OnFulfilled and onRejected need to be called asynchronously. Here, setTimeout is used to simulate async

Dealing with resolve of Promise

Const PENDING = 'pending'; const FULFILLED =' fulfilled'; const REJECTED = 'rejected'; class Promise {constructor (executor) {this.status = PENDING; this.value = undefined; this.reason = undefined; this.onResolvedCallbacks = []; this.onRejectedCallbacks = [] Let resolve = (value) = > {if (this.status = PENDING) {this.status = FULFILLED; this.value = value; this.onResolvedCallbacks.forEach ((fn) = > fn ());}} Let reject = (reason) = > {if (this.status = PENDING) {this.status = REJECTED; this.reason = reason; this.onRejectedCallbacks.forEach ((fn) = > fn ());}}; try {executor (resolve, reject) } catch (error) {reject (error);}} then (onFulfilled, onRejected) {/ / solve the problem that onFufilled,onRejected does not pass a value onFulfilled = typeof onFulfilled = = "function"? OnFulfilled: (v) = > v; / / because the value of the error is to be accessed later, an error should also be thrown here, otherwise onRejected = typeof onRejected = = "function" will be captured in the resolve of then later? OnRejected: (err) = > {throw err;} / / each call to then returns a new promise let promise2 = new Promise ((resolve, reject) = > {if (this.status = FULFILLED) {/ / Promise/A+ 2.2.4-- setTimeout setTimeout () = > {try {let x = onFulfilled (this.value)) / / x may be a proimise resolvePromise (promise2, x, resolve, reject);} catch (e) {reject (e);}}, 0) } if (this.status = REJECTED) {/ / Promise/A+ 2.2.3 setTimeout (() = > {try {let x = onRejected (this.reason); resolvePromise (promise2, x, resolve, reject) } catch (e) {reject (e);}, 0) } if (this.status = PENDING) {this.onResolvedCallbacks.push () = > {setTimeout () = > {try {let x = onFulfilled (this.value); resolvePromise (promise2, x, resolve, reject) } catch (e) {reject (e);}, 0);}) This.onRejectedCallbacks.push (() = > {setTimeout () = > {try {let x = onRejected (this.reason); resolvePromise (promise2, x, resolve, reject) } catch (e) {reject (e);}}, 0);});}}); return promise2 }} const resolvePromise = (promise2, x, resolve, reject) = > {/ / it is an error to wait for your own completion. With a type error, end promise Promise/A+ 2.3.1 if (promise2 = = x) {return reject ("Chaining cycle detected for promise #");} / / Promise/A+ 2.3.3.3.3 can only call let called once / / subsequent conditions should be strictly judged to ensure that the code can use if ((typeof x = = "object" & & x! = null) | | typeof x = "function") {try {/ / in order to determine that the resolve is passed, there is no need to reject (for example, when reject and resolve are called at the same time) Promise/A+ 2.3.3.1 let then = x.then If (typeof then = = "function") {/ / Don't write it as x.then, just then.call, because x.then will take the value again. Object.defineProperty Promise/A+ 2.3.3.3 then.call (x, (y) = > {/ / decide whether to succeed or fail based on the status of promise if (called) return Called = true; / / the process of recursive parsing (because there may be promise in promise) Promise/A+ 2.3.3.3.1 resolvePromise (promise2, y, resolve, reject) }, (r) = > {/ / fail whenever you fail Promise/A+ 2.3.3.3.2 if (called) return; called = true; reject (r);}) } else {/ / if x.then is a normal value, directly return resolve as the result Promise/A+ 2.3.3.4 resolve (x);}} catch (e) {/ / Promise/A+ 2.3.3.2 if (called) return; called = true Reject (e);}} else {/ / if x is a normal value, directly return resolve as the result Promise/A+ 2.3.4 resolve (x);}}

After Promise is finished, we can test the code we wrote through the promises-aplus-tests package to see if it conforms to the A+ specification. However, you need to add a piece of code before the test:

/ / promise.js / / here are all the Promise codes written above: Promise.defer = Promise.deferred = function () {let dfd = {} dfd.promise = new Promise ((resolve,reject) = > {dfd.resolve = resolve; dfd.reject = reject;}); return dfd;} module.exports = Promise

Global installation:

Npm I promises-aplus-tests-g

Execute the verification command under the terminal:

Promises-aplus-tests promise.js

The code written above can pass all 872 test cases smoothly.

Reference:

BAT front-end classic interview questions: the most detailed handwritten Promise tutorial in history [8]

100 lines of code to implement the Promises/A+ specification [9]

Promise.resolve

Promsie.resolve (value) can convert any value to a Promise whose value state is fulfilled, but returns it as is if the value passed in itself is Promise.

Promise.resolve = function (value) {/ / if it is Promsie, output it directly if (value instanceof Promise) {return value} return new Promise (resolve = > resolve (value))}

Reference: in-depth understanding of Promise [10]

Promise.reject

Similar to Promise.resolve (), Promise.reject () instantiates a Promise in the rejected state. But unlike Promise.resolve (), if you pass a Promise object to Promise.reject (), that object becomes the value of the new Promise.

Promise.reject = function (reason) {return new Promise ((resolve, reject) = > reject (reason))} Promise.all

The rules of Promise.all are as follows:

If all Promsie passed in are fulfilled, a new Promise consisting of their values with a status of fulfilled is returned

As long as one Promise is rejected, the new Promsie of the rejected status is returned, and its value is the value of the Promise of the first rejected

As long as one Promise is pending, a new Promise with pending status is returned

Promise.all = function (promiseArr) {let index = 0, result = [] return new Promise ((resolve, reject) = > {promiseArr.forEach ((p I) = > {Promise.resolve (p) .then (val = > {index++ result [I] = val if (index = promiseArr.length) {resolve (result)}} Err = > {reject (err)} Promise.race

Promise.race returns a new instance wrapped by the first instance of fulfilled or rejected of all iterable instances.

Promise.race = function (promiseArr) {return new Promise ((resolve, reject) = > {promiseArr.forEach (p = > {Promise.resolve (p) .then (val = > {resolve (val)}, err = > {rejecte (err)} Promise.allSettled)

The rules of Promise.allSettled are as follows:

If the state of all Promise has changed, then a new state is returned as Promise of fulfilled, and its value is an array. Each item in the array is an object consisting of the values and states of all Promise.

If there is a Promise of pending, a new instance with a status of pending is returned

Promise.allSettled = function (promiseArr) {let result = [] return new Promise ((resolve, reject) = > {promiseArr.forEach ((p, I) = > {Promise.resolve (p) .then (val = > {result.push ({status: 'fulfilled') Value: val}) if (result.length = promiseArr.length) {resolve (result)}}, err = > {result.push ({status: 'rejected' Reason: err}) if (result.length = promiseArr.length) {resolve (result)}})} Promise.any

The rules of Promise.any are as follows:

If the empty array or all Promise are rejected, the new Promsie with the status of rejected and the error with the value of AggregateError are returned

As long as one is in fulfilled status, the first new instance of fulfilled is returned.

In other cases, a new instance of pending is returned

Promise.any = function (promiseArr) {let index = 0 return new Promise ((resolve, reject) = > {if (promiseArr.length = 0) return promiseArr.forEach ((p, I) = > {Promise.resolve (p) .then (val = > {resolve (val)} Err = > {index++ if (index = promiseArr.length) {reject (new AggregateError ('All promises were rejected'))}})} "what are the inheritance types of JS"? Thank you for your reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 252

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report