「シニアフロントエンドインタビュー」手書きのJavaScriptコードは無敵攻略です

転送:  https://blog.csdn.net/sinat_17775997/article/details/88902756

 

 

手書きナビゲーションパス

1.達成するためにnewオペレータを

出典:英語で「JavaScriptを知りません」

newオペレータは、これらのことを実行します。

  • これは、新しいオブジェクトを作成します。
  • これは、実行されます[[Prototype]](つまり__proto__)のリンクを。
  • それは作るthis新しく作成されたオブジェクトにポイントを。
  • new作成されたすべてのオブジェクト最終的にされる[[Prototype]]の機能にリンクされprototypeたオブジェクト。
  • 関数はオブジェクトタイプ返さない場合Object(付属のFunctoin, Array, Date, RegExg, Error)、その後、new関数呼び出し式は、オブジェクト参照を返します。
 
  1. function New(func) {

  2. var res = {};

  3. if (func.prototype !== null) {

  4. res.__proto__ = func.prototype;

  5. }

  6. var ret = func.apply(res, Array.prototype.slice.call(arguments, 1));

  7. if ((typeof ret === "object" || typeof ret === "function") && ret !== null) {

  8. return ret;

  9. }

  10. return res;

  11. }

  12. var obj = New(A, 1, 2);

  13. // equals to

  14. var obj = new A(1, 2);

  15. 复制代码

2.実装JSON.stringify

JSON.stringify(value[, replacer [, space]])

  • Boolean | Number| String タイプは自動的に対応する元の値に変換されます。
  • undefined、任意の機能はsymbol、無視されるか、に変換される(非配列プロパティは、オブジェクトの値がときに表示される)  null(配列で起こります)。
  • 非可算のプロパティは無視されます
  • オブジェクトのプロパティの値は、いくつかの間接的な方法で、オブジェクト自体に戻って参照する場合、それは循環参照され、プロパティは無視されます。
 
  1. function jsonStringify(obj) {

  2. let type = typeof obj;

  3. if (type !== "object" || type === null) {

  4. if (/string|undefined|function/.test(type)) {

  5. obj = '"' + obj + '"';

  6. }

  7. return String(obj);

  8. } else {

  9. let json = []

  10. arr = (obj && obj.constructor === Array);

  11. for (let k in obj) {

  12. let v = obj[k];

  13. let type = typeof v;

  14. if (/string|undefined|function/.test(type)) {

  15. v = '"' + v + '"';

  16. } else if (type === "object") {

  17. v = jsonStringify(v);

  18. }

  19. json.push((arr ? "" : '"' + k + '":') + String(v));

  20. }

  21. return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}")

  22. }

  23. }

  24. jsonStringify({x : 5}) // "{"x":5}"

  25. jsonStringify([1, "false", false]) // "[1,"false",false]"

  26. jsonStringify({b: undefined}) // "{"b":"undefined"}"

  27. 复制代码

3.達成するためにJSON.parse

JSON.parse(text[, reviver])

値またはオブジェクトのJavaScriptのJSON文字列を解析するために、文字列の構成で説明。オブジェクトの変換(演算)を実行するためのオプションリバイバー関数が戻る前に得られます。

3.1最初:直接呼び出しはeval

 
  1. function jsonParse(opt) {

  2. return eval('(' + opt + ')');

  3. }

  4. jsonParse(jsonStringify({x : 5}))

  5. // Object { x: 5}

  6. jsonParse(jsonStringify([1, "false", false]))

  7. // [1, "false", falsr]

  8. jsonParse(jsonStringify({b: undefined}))

  9. // Object { b: "undefined"}

  10. 复制代码

不必要に使用しないでください  eval、のeval()関数は、彼が人々を運ぶために権利を持っている危険なコードの実行です。あなたは()のコードの文字列が悪意のある関係者(悪意のある人物)コントロールチェンジで実行されますevalを使用する場合は、ユーザーのコンピュータ上で悪質なコードを実行するために、あなたのページ/拡張右手続きに終わる可能性の下で。

これは、JSコード、XSSの脆弱性を実行します。

あなたがこの方法を覚えておきたい場合は、パラメータのJSONを確認するために行う必要があります。

 
  1. var rx_one = /^[\],:{}\s]*$/;

  2. var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;

  3. var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;

  4. var rx_four = /(?:^|:|,)(?:\s*\[)+/g;

  5. if (

  6. rx_one.test(

  7. json

  8. .replace(rx_two, "@")

  9. .replace(rx_three, "]")

  10. .replace(rx_four, "")

  11. )

  12. ) {

  13. var obj = eval("(" +json + ")");

  14. }

  15. 复制代码

3.2秒:機能

ソース  の新しい機能を持つ魔法のeval()を()

コア:同じ文字列パラメータ特性。Functioneval

var func = new Function(arg1, arg2, ..., functionBody);

実際には、JSONの変換は、あなただけそうする必要があります。

 
  1. var jsonStr = '{ "age": 20, "name": "jack" }'

  2. var json = (new Function('return ' + jsonStr))();

  3. 复制代码

eval そして、  Function jsのコードでは、動的な役割をコンパイルしているが、実際のプログラミングでの使用は推奨されません。

ここでは、2つが十分で書いて、面接指向プログラミングです。再帰と関連原則面倒な状態マシンを含む、第三、第四として、具体的には見ることができます。

「三つの方法をJSON.parse」

4. 1を実装しますcallか、 apply

call構文:

fun.call(thisArg, arg1, arg2, ...)、関数呼び出し、これは別個に設けられ、指定された値とパラメータ(パラメータリスト)を有します。

apply構文:

func.apply(thisArg, [argsArray])、関数呼び出し、ならびにアレイ(またはオブジェクトの類似配列)提供されるパラメータ。

4.1  Function.callルーチンを実現することにより

callコア:

  • 機能設定オブジェクトのプロパティ
  • &この機能を実行削除
  • 指定されたthis特定のパラメータの受け渡しの機能を機能し、実行します
  • デフォルトのウィンドウを指し、パラメータを渡さない場合

なぜそれを達成するために日常的であると言いますか?本当のインタビューなので、インタビュアーは、あなたは徐々にあなたは、彼の日常に対抗することができ、簡単なバージョンを書くために、この時間を深い配慮を手放すのお気に入り:

シンプルバージョン4.1.1

 
  1. var foo = {

  2. value: 1,

  3. bar: function() {

  4. console.log(this.value)

  5. }

  6. }

  7. foo.bar() // 1

  8. 复制代码

パーフェクトバージョン4.1.2

インタビュアーは、さらに質問があるか、この時点で考えることをふりをすることができた場合。次に、以下のバージョンを記述します。

 
  1. Function.prototype.call2 = function(content = window) {

  2. content.fn = this;

  3. let args = [...arguments].slice(1);

  4. let result = content.fn(...args);

  5. delect content.fn;

  6. return result;

  7. }

  8. var foo = {

  9. value: 1

  10. }

  11. function bar(name, age) {

  12. console.log(name)

  13. console.log(age)

  14. console.log(this.value);

  15. }

  16. bar.call2(foo, 'black', '18') // black 18 1

  17. 复制代码

4.2  Function.applyアナログ実現

apply()実現とcall()似ているが、異なる引数。直接バーコードに添付:

 
  1. Function.prototype.apply2 = function(context = window) {

  2. context.fn = this

  3. let result;

  4. // 判断是否有第二个参数

  5. if(arguments[1]) {

  6. result = context.fn(...arguments[1])

  7. } else {

  8. result = context.fn()

  9. }

  10. delete context.fn()

  11. return result

  12. }

  13. 复制代码

5.達成するためにFunction.bind()

bind()方法:

これは、新しい関数を作成します。この新しい関数が呼び出されると、この最初のパラメータのバインドは()それが実行され、シーケンスパラメータの後に前の引数で、引数として渡されますよう。(MDNから)

また、bind実装はインスタンス化プロトタイプチェーンへの影響を考慮する必要があります。

 
  1. Function.prototype.bind2 = function(content) {

  2. if(typeof this != "function") {

  3. throw Error("not a function")

  4. }

  5. // 若没问参数类型则从这开始写

  6. let fn = this;

  7. let args = [...arguments].slice(1);

  8.  
  9. let resFn = function() {

  10. return fn.apply(this instanceof resFn ? this : content,args.concat(...arguments) )

  11. }

  12. function tmp() {}

  13. tmp.prototype = this.prototype;

  14. resFn.prototype = new tmp();

  15.  
  16. return resFn;

  17. }

  18. 复制代码

6.連続を実装

組み合わせ寄生継承

一般的にはそうでない場合は継承が最初のインスタンスで二回親クラスのコンストラクタを呼び出すか、他の欠点を持ってしまうため、これを書くことをお勧め。

コアの実装は次のとおりです。で  F 、空のコンストラクタの実装置き換えるために  Parent 、このコンストラクタを。

 
  1. function Parent(name) {

  2. this.name = name;

  3. }

  4. Parent.prototype.sayName = function() {

  5. console.log('parent name:', this.name);

  6. }

  7. function Child(name, parentName) {

  8. Parent.call(this, parentName);

  9. this.name = name;

  10. }

  11. function create(proto) {

  12. function F(){}

  13. F.prototype = proto;

  14. return new F();

  15. }

  16. Child.prototype = create(Parent.prototype);

  17. Child.prototype.sayName = function() {

  18. console.log('child name:', this.name);

  19. }

  20. Child.prototype.constructor = Child;

  21.  
  22. var parent = new Parent('father');

  23. parent.sayName(); // parent name: father

  24.  
  25. var child = new Child('son', 'father');

  26. 复制代码

7. JS関数カリー化を達成するために

 

 

何をカリー化されましたか?

 

コンピュータサイエンスでは、カリー(カリー化)は、複数のパラメータを受信する機能は、単一のパラメータ(第関数の最初のパラメータ)を受け付ける関数に変換され、新しい機能が残りの引数を返す受け入れ、結果を返します技術。

役割の主な機能とカリー化機能は、パラメータの再利用で、初期および遅延実行を返します。

7.1 通用版

 
  1. function curry() {

  2. var args = Array.prototype.slice.call(arguments);

  3. var fn = function() {

  4. var newArgs = args.concat(Array.prototype.slice.call(arguments));

  5. return multi.apply(this, newArgs);

  6. }

  7. fn.toString = function() {

  8. return args.reduce(function(a, b) {

  9. return a * b;

  10. })

  11. }

  12. return fn;

  13. }

  14. function multiFn(a, b, c) {

  15. return a * b * c;

  16. }

  17.  
  18. var multi = curry(multiFn);

  19.  
  20. multi(2)(3)(4);

  21. multi(2,3,4);

  22. multi(2)(3,4);

  23. multi(2,3)(4);

  24. 复制代码

7.2  ES6サンの言葉遣い

 
  1. const curry = (fn, arr = []) => (...args) => (

  2. arg => arg.length === fn.length

  3. ? fn(...arg)

  4. : curry(fn, arg)

  5. )([...arr, ...args])

  6.  
  7. let curryTest=curry((a,b,c,d)=>a+b+c+d)

  8. curryTest(1,2,3)(4) //返回10

  9. curryTest(1,2)(4)(3) //返回10

  10. curryTest(1,2)(3,4) //返回10

  11. 复制代码

8.手書きPromise(シニア義務)

のは、上で行こうPromise/A+仕様:

  • 三州pending| fulfilled(resolved) | rejected
  • するとpending、時間の状態、それがに転送することができますfulfilled(resolved)またはrejected状態
  • ときにおけるfulfilled(resolved)状態またはrejectedときの状態、変数ではありません。
  1. が存在する必要がありthen、非同期実行する方法thenの2つのパラメータを取り、約束を返す必要があります。
 
  1. // onFulfilled 用来接收promise成功的值

  2. // onRejected 用来接收promise失败的原因

  3. promise1=promise.then(onFulfilled, onRejected);

  4. 复制代码

8.1  Promiseのフローチャート

 

 

下確認するPromise使用方法:

 

 
  1. var promise = new Promise((resolve,reject) => {

  2. if (操作成功) {

  3. resolve(value)

  4. } else {

  5. reject(error)

  6. }

  7. })

  8. promise.then(function (value) {

  9. // success

  10. },function (value) {

  11. // failure

  12. })

  13. 复制代码

インタビュー十分バージョン8.2

出典:約束/ A +の仕様の約束と完全に矛盾しを達成

 
  1. function myPromise(constructor){

  2. let self=this;

  3. self.status="pending" //定义状态改变前的初始状态

  4. self.value=undefined;//定义状态为resolved的时候的状态

  5. self.reason=undefined;//定义状态为rejected的时候的状态

  6. function resolve(value){

  7. //两个==="pending",保证了状态的改变是不可逆的

  8. if(self.status==="pending"){

  9. self.value=value;

  10. self.status="resolved";

  11. }

  12. }

  13. function reject(reason){

  14. //两个==="pending",保证了状态的改变是不可逆的

  15. if(self.status==="pending"){

  16. self.reason=reason;

  17. self.status="rejected";

  18. }

  19. }

  20. //捕获构造异常

  21. try{

  22. constructor(resolve,reject);

  23. }catch(e){

  24. reject(e);

  25. }

  26. }

  27. 复制代码

同時に、それは必要であるmyPromiseプロトタイプの定義上の連鎖の呼び出しthen方法:

 
  1. myPromise.prototype.then=function(onFullfilled,onRejected){

  2. let self=this;

  3. switch(self.status){

  4. case "resolved":

  5. onFullfilled(self.value);

  6. break;

  7. case "rejected":

  8. onRejected(self.reason);

  9. break;

  10. default:

  11. }

  12. }

  13. 复制代码

测试一下:

 
  1. var p=new myPromise(function(resolve,reject){resolve(1)});

  2. p.then(function(x){console.log(x)})

  3. //输出1

  4. 复制代码

8.3 大厂专供版

直接贴出来吧,这个版本还算好理解

 
  1. const PENDING = "pending";

  2. const FULFILLED = "fulfilled";

  3. const REJECTED = "rejected";

  4.  
  5. function Promise(excutor) {

  6. let that = this; // 缓存当前promise实例对象

  7. that.status = PENDING; // 初始状态

  8. that.value = undefined; // fulfilled状态时 返回的信息

  9. that.reason = undefined; // rejected状态时 拒绝的原因

  10. that.onFulfilledCallbacks = []; // 存储fulfilled状态对应的onFulfilled函数

  11. that.onRejectedCallbacks = []; // 存储rejected状态对应的onRejected函数

  12.  
  13. function resolve(value) { // value成功态时接收的终值

  14. if(value instanceof Promise) {

  15. return value.then(resolve, reject);

  16. }

  17. // 实践中要确保 onFulfilled 和 onRejected 方法异步执行,且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。

  18. setTimeout(() => {

  19. // 调用resolve 回调对应onFulfilled函数

  20. if (that.status === PENDING) {

  21. // 只能由pending状态 => fulfilled状态 (避免调用多次resolve reject)

  22. that.status = FULFILLED;

  23. that.value = value;

  24. that.onFulfilledCallbacks.forEach(cb => cb(that.value));

  25. }

  26. });

  27. }

  28. function reject(reason) { // reason失败态时接收的拒因

  29. setTimeout(() => {

  30. // 调用reject 回调对应onRejected函数

  31. if (that.status === PENDING) {

  32. // 只能由pending状态 => rejected状态 (避免调用多次resolve reject)

  33. that.status = REJECTED;

  34. that.reason = reason;

  35. that.onRejectedCallbacks.forEach(cb => cb(that.reason));

  36. }

  37. });

  38. }

  39.  
  40. // 捕获在excutor执行器中抛出的异常

  41. // new Promise((resolve, reject) => {

  42. // throw new Error('error in excutor')

  43. // })

  44. try {

  45. excutor(resolve, reject);

  46. } catch (e) {

  47. reject(e);

  48. }

  49. }

  50.  
  51. Promise.prototype.then = function(onFulfilled, onRejected) {

  52. const that = this;

  53. let newPromise;

  54. // 处理参数默认值 保证参数后续能够继续执行

  55. onFulfilled =

  56. typeof onFulfilled === "function" ? onFulfilled : value => value;

  57. onRejected =

  58. typeof onRejected === "function" ? onRejected : reason => {

  59. throw reason;

  60. };

  61. if (that.status === FULFILLED) { // 成功态

  62. return newPromise = new Promise((resolve, reject) => {

  63. setTimeout(() => {

  64. try{

  65. let x = onFulfilled(that.value);

  66. resolvePromise(newPromise, x, resolve, reject); // 新的promise resolve 上一个onFulfilled的返回值

  67. } catch(e) {

  68. reject(e); // 捕获前面onFulfilled中抛出的异常 then(onFulfilled, onRejected);

  69. }

  70. });

  71. })

  72. }

  73.  
  74. if (that.status === REJECTED) { // 失败态

  75. return newPromise = new Promise((resolve, reject) => {

  76. setTimeout(() => {

  77. try {

  78. let x = onRejected(that.reason);

  79. resolvePromise(newPromise, x, resolve, reject);

  80. } catch(e) {

  81. reject(e);

  82. }

  83. });

  84. });

  85. }

  86.  
  87. if (that.status === PENDING) { // 等待态

  88. // 当异步调用resolve/rejected时 将onFulfilled/onRejected收集暂存到集合中

  89. return newPromise = new Promise((resolve, reject) => {

  90. that.onFulfilledCallbacks.push((value) => {

  91. try {

  92. let x = onFulfilled(value);

  93. resolvePromise(newPromise, x, resolve, reject);

  94. } catch(e) {

  95. reject(e);

  96. }

  97. });

  98. that.onRejectedCallbacks.push((reason) => {

  99. try {

  100. let x = onRejected(reason);

  101. resolvePromise(newPromise, x, resolve, reject);

  102. } catch(e) {

  103. reject(e);

  104. }

  105. });

  106. });

  107. }

  108. };

  109. 复制代码

 

 

emmm,我还是乖乖地写回进阶版吧。

 

9. 手写防抖(Debouncing)和节流(Throttling)

scroll 事件本身会触发页面的重新渲染,同时 scroll 事件的 handler 又会被高频度的触发, 因此事件的 handler 内部不应该有复杂操作,例如 DOM 操作就不应该放在事件处理中。 针对此类高频度触发事件问题(例如页面 scroll ,屏幕 resize,监听用户输入等),有两种常用的解决方法,防抖和节流。

9.1 防抖(Debouncing)实现

典型例子:限制 鼠标连击 触发。

一个比较好的解释是:

当一次事件发生后,事件处理器要等一定阈值的时间,如果这段时间过去后 再也没有 事件发生,就处理最后一次发生的事件。假设还差 0.01 秒就到达指定时间,这时又来了一个事件,那么之前的等待作废,需要重新再等待指定时间。

 

 

 

 
  1. // 防抖动函数

  2. function debounce(fn,wait=50,immediate) {

  3. let timer;

  4. return function() {

  5. if(immediate) {

  6. fn.apply(this,arguments)

  7. }

  8. if(timer) clearTimeout(timer)

  9. timer = setTimeout(()=> {

  10. fn.apply(this,arguments)

  11. },wait)

  12. }

  13. }

  14. 复制代码

9.2 节流(Throttling)实现

可以理解为事件在一个管道中传输,加上这个节流阀以后,事件的流速就会减慢。实际上这个函数的作用就是如此,它可以将一个函数的调用频率限制在一定阈值内,例如 1s,那么 1s 内这个函数一定不会被调用两次

 

 

 

简单的节流函数:

 
  1. function throttle(fn, wait) {

  2. let prev = new Date();

  3. return function() {

  4. const args = arguments;

  5. const now = new Date();

  6. if (now - prev > wait) {

  7. fn.apply(this, args);

  8. prev = new Date();

  9. }

  10. }

  11. 复制代码

9.3 结合实践

通过第三个参数来切换模式。

 
  1. const throttle = function(fn, delay, isDebounce) {

  2. let timer

  3. let lastCall = 0

  4. return function (...args) {

  5. if (isDebounce) {

  6. if (timer) clearTimeout(timer)

  7. timer = setTimeout(() => {

  8. fn(...args)

  9. }, delay)

  10. } else {

  11. const now = new Date().getTime()

  12. if (now - lastCall < delay) return

  13. lastCall = now

  14. fn(...args)

  15. }

  16. }

  17. }

  18. 复制代码

10. 手写一个JS深拷贝

有个最著名的乞丐版实现,在《你不知道的JavaScript(上)》里也有提及:

 

 

 

10.1 乞丐版

 
  1. var newObj = JSON.parse( JSON.stringify( someObj ) );

  2. 复制代码

10.2 面试够用版

 
  1. function deepCopy(obj){

  2. //判断是否是简单数据类型,

  3. if(typeof obj == "object"){

  4. //复杂数据类型

  5. var result = obj.constructor == Array ? [] : {};

  6. for(let i in obj){

  7. result[i] = typeof obj[i] == "object" ? deepCopy(obj[i]) : obj[i];

  8. }

  9. }else {

  10. //简单数据类型 直接 == 赋值

  11. var result = obj;

  12. }

  13. return result;

  14. }

  15. 复制代码

关于深拷贝的讨论天天有,这里就贴两种吧,毕竟我...

 

 

 

11.实现一个instanceOf

 
  1. function instanceOf(left,right) {

  2.  
  3. let proto = left.__proto__;

  4. let prototype = right.prototype

  5. while(true) {

  6. if(proto === null) return false

  7. if(proto === prototype) return true

  8. proto = proto.__proto__;

  9. }

  10. }

  11.  
  12. 复制代码

 


作者:前端劝退师
链接:https://juejin.im/post/5c9c3989e51d454e3a3902b6
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

おすすめ

転載: blog.csdn.net/qq_36688928/article/details/91419118