Ajax 请求的步骤 第一步(得到 XMLHttpRequest)
第二步(打开与服务器的链接)
第三步(发送请求)
xmlHttp.send(null);如果括号里不给 null 可能会造成部分浏览器无法发送;
第四步
在 xmlHttp 对象的一个事件上注册监听器: onreadstatechange
得到的 5 个状态
>0状态:刚创建,还没有调用 open() 方法;
>1状态:请求刚开始,调用了open()方法,但是没有调用 send 方法;
>2状态:调用完了 send() 方法;
>3状态:服务器已经开始响应。但不表示响应结束;
>4状态:服务器响应结束!;
得到 xmlHttp 对象状态
var state = xmlHttp.redayState;  // 可能是 0,1,2,3,4
得到服务器的响应状态码
var state = xmlHttp.status;  // 可能是 200、404、500
得到服务器的响应内容
Ajax 的缺点 (1)、ajax 不支持浏览器 back 按钮
(2)、安全问题 ajax 暴露了与服务器交互的细节。
(3)、对搜索引擎的支持比较弱
(4)、破坏了程序的异常机制
什么是 Ajax 和 JSON,它们的优缺点 Ajax 全称 asynchronous JavaScript and XML,即异步 JavaScript 和 xml,用于在 web 页面中事先异步数据交互,实现页面局部刷新。
优点:可以使得页面不重载全部内容的情况下加载局部内容,降低数据传输量,避免用户不断刷新或者跳转页面,提高用户体验。
缺点:对搜索引擎不友好,要实现 ajax 下的前后退功能成本较大,可能造成请求数量增加跨域问题限制;
json 是一种轻量级的数据交换格式,ECMA 的一个子级
优点:轻量级、易于人的阅读和编写,便于机器(JavaScript)解析,支持复合数据类型(数组、对象、字符串、数字)
Ajax 请求的时候 get 和 post 方式的区别 get 一般用来进行查询操作,url 地址有长度限制,请求的参数都暴露在 url 地址中,如果传递中文参数,需要自己进行编码操作,安全性低
post 请求方式主要提交数据,没有数据长度的限制,提交的数据内容存在于 http 请求体中,数据不会暴露在 url 地址中。
jsonp的原理,以及为什么不是真正的 ajax jsonp 并不是一种数据格式,而 json 是一种数据格式,jsonp 是用来解决跨域获取数据的一种解决方案,具体是通过动态创建 script 标签,然后通过标签的 src 属性获取 js 文件中的 js 脚本,该脚本的内容是一个函数调用,参数就是服务器返回的数据,为了处理这些返回的数据,需要事先在页面定义好回调函数,本质上不是使用 ajax 技术。
阐述一下异步加载 (1) 异步加载的方案:动态插入 script 标签
(2) 通过 ajax 去获取 js 代码,然后通过 eval 执行
(3) script 标签上添加 defer 或 async 属性
(4) 创建并插入 iframe,让它异步执行 js
post 和 get 的区别,何时使用 post get:一般用于信息获取,使用 url 传递参数,对所有发送信息的数量也有限制,一般在 2000 个字符,有的浏览器 8000 个字符
post:一般用于修改服务器上的资源,对于发送的信息没有限制
在以下情况中,请使用 post 请求:
(1) 无法使用缓存文件(更新服务器上的文件或数据库)
(2) 向服务器发送大量数据(post 没有数据限制)
(3) 发送包含未知字符的用户输入时,post 比 get 更稳定也更可靠
数组去重的方法 排序方法
1 2 3 4 5 6 7 8 9 10 function  distinct (arr )    var  result = []     for (var  i = 0 ;i < arr.length; i++){         for (var  j = i+1 ;j<arr.length;j++){             j = ++i;         }         result.push(arr[i])     }     return  result; } 
对象方法
1 2 3 4 5 6 7 8 9 10 11 function  unique (arr )    var  res = []     var  json = {}     for (var  i=0 ;i<arr.length;i++){         if (!json[arr[i]]){             res.push(arr[i])             json[arr[i]] = 1 ;         }     }     return  res; } 
js 的作用域 作用域说明:一般理解指一个变量的作用范围
1、全局作用域
(1)全局作用域在页面打开时被创建,页面关闭时被销毁
(2)编写在 script 标签中的变量和函数,作用域为全局,在页面的任意位置都可以访问到
(3)在全局作用域中有全局对象 window,代表一个浏览器窗口,由浏览器创建,可以直接调用
(4)全局作用域中声明的变量和函数会作为 window 对象的属性和方法保存
2、函数作用域
(1)调用函数时,函数作用域被创建,函数执行完毕,函数作用域被销毁
(2)每调用一次函数就会被创建一个新的函数作用域,他们之间是相互独立的。
(3)在函数作用域中可以访问到全局作用域的变量,在函数外无法访问到函数作用域内的变量
(4)在函数作用域中访问变量、函数时,会先在自身作用域中寻找,若没有找到,则会到函数的上一级作用域中寻找,一直到全局作用域
说说你对闭包的理解 说说你对闭包的理解 使用闭包主要是为了设计私有的方法和变量。
闭包的优点是可以避免全局变量的污染,
缺点是闭包会常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。
闭包有三个特性:
1、函数嵌套函数
2、函数内部可以引用外部的参数和比变量
3、参数和变量不会被垃圾回收机制回收
请你谈谈 Cookie 的弊端 cookie 虽然在持久保存客户数据提供了方便,分担了服务器存储的负担,但是有很多局限性。
1、每个域名下最多生成 20 个 cookie.
2、浏览器会清理 cookie ,IE、Opera 会清理近期最少使用的 cookie,Firefox 会随机清理 cookie.
3、cookie 最大大约为 4096 字节。
优点:极高的扩展性和可用性。
1、通过良好的编程,控制保存在 cookie 中的 session 对象的大小。
2、通过加密和安全传输技术(SSL),减少 cookie 被破解的可能性。
3、只在 cookie 中存放不敏感数据,即使被盗也不会有重大损失。
4、控制 cookie 的生命期,使之不会永远有效。盗窃者很可能拿到一个过期的 cookie。
缺点:
1、cookie 数量和长度的限制。
2、安全性问题。
3、有些状态不可能保存在客户端。
web storage和 cookie 的区别 cookie 和 session 的区别 1、cookie 数据存放在客户的浏览器上,session 数据放在服务器上。
2、cookie 不是很安全,可以分析存放在本地的 cookie 并进行 cookie 欺骗,考虑到安全应当使用 session。
3、session 会在一定时间内保存在服务器上。当访问增多,会比较占用服务器的性能。
4、单个 cookie 保存的数据不能超过 4k,一个站点最多保存20个 cookie。
使用 typeof bar === “object” 来确定 bar 是否是对象的潜在陷阱是什么?如何避免这个陷阱? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var  bar = null ;console .log((bar !== null ) && (typeof  bar === "object" ));  var  bar = function (}; console .log((bar !== null ) && ((typeof  bar === "object" ) || (typeof  bar === "function" )));var  bar = [];console .log((bar !== null ) && (typeof  bar === "object" ) && (toString.call(bar) !== "[object Array]" ));console .log((bar !== null ) && (typeof  bar === "object" ) && (! $.isArray(bar)));
下面的代码将输出什么到控制台,为什么 1 2 3 4 5 6 7 8 9 10 (function (   var  a = b = 3 ;  })(); console .log("a defined? "  + (typeof  a !== 'undefined' ));console .log("b defined? "  + (typeof  b !== 'undefined' ));a defined? false  b defined? true  
下面输出什么到控制台,为什么 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 var  myObject = {  foo: "bar" ,   func: function (     var  self = this ;     console .log("outer func:  this.foo = "  + this .foo);     console .log("outer func:  self.foo = "  + self.foo);     (function (       console .log("inner func:  this.foo = "  + this .foo);       console .log("inner func:  self.foo = "  + self.foo);     }());   } }; myObject.func(); 
封装JavaScript源文件的全部内容到一个函数块有什么意义及理由 这是一个越来越普遍的做法,被许多流行的JavaScript库(jQuery,Node.js等)采用。这种技术创建了一个围绕文件全部内容的闭包,也许是最重要的是,创建了一个私有的命名空间,从而有助于避免不同JavaScript模块和库之间潜在的名称冲突。
这种技术的另一个特点是,允许一个易于引用的(假设更短的)别名用于全局变量。这通常用于,例如,jQuery插件中。jQuery允许你使用jQuery.noConflict(),来禁用 $ 引用到jQuery命名空间。在完成这项工作之后,你的代码仍然可以使用$ 利用这种闭包技术,如下所示:
1 (function ($ )  
在 JavaScript 源文件的开头包含 use strict 意义和好处
use strict 是一种在JavaScript代码运行时自动实行更严格解析和错误处理的方法。
严格模式的一些主要优点包括:
使调试更加容易。 
防止意外的全局变量。 
消除 this 强制 
不允许重复的属性名称或参数值。 
使eval() 更安全。 
在 delete使用无效时抛出错误。 
 
考虑以下两个函数。它们会返回相同的东西吗? 为什么相同或为什么不相同 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 function  foo1 (  return  {       bar: "hello"    }; } function  foo2 (  return    {     bar: "hello"    }; } console .log(foo1());console .log(foo2());
NaN 是什么?它的类型是什么?你如何可靠地测试一个值是否等于 NaN NaN 属性代表一个“不是数字”的值。这个特殊的值是因为运算不能执行而导致的,不能执行的原因要么是因为其中的运算对象之一非数字(例如, “abc” / 4),要么是因为运算的结果非数字(例如,除数为零)。
NaN 是 Number;
检测
下列代码将输出什么?并解释原因 console.log(0.1 + 0.2);
JavaScript中的数字和浮点精度的处理相同,因此,可能不会总是产生预期的结果。
9.讨论写函数 isInteger(x) 的可能方法,用于确定x是否是整数
在ECMAScript规格说明中,整数只概念上存在:即,数字值总是存储为浮点值。
function isInteger(x) { return (x^0) === x; }
Math.ceil() 和 Math.floor() 在上面的实现中等同于 Math.round()。
下列代码行1-4如何排序,使之能够在执行代码时输出到控制台? 为什么 (function() {
output
浏览器有一个事件循环,会检查事件队列和处理未完成的事件。例如,如果时间发生在后台(例如,脚本的 onload 事件)时,浏览器正忙(例如,处理一个 onclick),那么事件会添加到队列中。当onclick处理程序完成后,检查队列,然后处理该事件(例如,执行 onload 脚本)。
写一个简单的函数(少于80个字符),要求返回一个布尔值指明字符串是否为回文结构 function isPalindrome(str) {
console.log(isPalindrome(“level”));                   // logs ‘true’
写一个 sum方法,在使用下面任一语法调用时,都可以正常工作 console.log(sum(2,3));   // Outputs 5
柯里化
1 2 3 4 5 6 7 function sum(x) {   if (arguments.length == 2) {     return arguments[0] + arguments[1];   } else {     return function(y) { return x + y; };   } } 
1 2 3 4 5 6 7 function  sum (x, y )   if  (y !== undefined ) {     return  x + y;   } else  {     return  function (y ) return  x + y; };   } } 
下面console的输出结果是。
1 2 3 4 5 6 7 8 var  name = “one”;var  User =function  (  )   this .name = “two”; } var  obj =User( ); console .log(name);
答案:two
阅读如下代码,请问两处console的输出结果。
1 2 3 4 5 6 7 8 9 10 var  Product = {  count: 1 ,   getCount: function (  )      return  this .count++;   } }; console .log(Product.getCount( ));var  func = Product.getCount;console .log(func( ));
答案: 1 NaN
Array.prototype.slice.call(arr,2)方法的作用是: 答案:以arr为基础,并调用其slice方法,截取从索引为2到末尾位置
http请求中GET和POST方法的区别是()。 答案:
简述浏览器发起一个网络请求(HTTP请求事务)后,都经历了哪些步骤: 输入网址:输入url地址
请为所有数组对象添加一个通用的 remove 方法,参数是数组元素的索引值,实现删除指定数组元素的索引的功能。(可以写伪代码)。例如:var arr=[1,2,3,4,5,6]; arr.remove(3); 修改后的arr为[1,2,3,5,6]。 1 2 3 4 5 6 7 8 9 10 Array .prototype.remove= function (i )  if (isNaN (i) || i < 0  || i >=this .length){     return  this ;   }   for (var  j=i; j<this .length-1 ; j++){     this [j] = this [j+1 ];   }   this .length-=1 ; }; 
手写 实现一个 new 操作符
new 操作符做了这些事:
它创建了一个全新的对象 
它会被执行 [[Prototype]] (也就是 __proto__)链接 
它使 this 指定新创建的对象 
通过 new 创建的每个对象将最终被 [[Prototype]] 链接到这个函数的 prototype 对象上 
如果函数没有返回对象类型 Object (包含 Function,Array,Date,RegExg,Error),那么 new 表达式中的函数调用将返回该对象引用 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function  New (func )   var  res = {};   if (func.prototype !== null ){     res.__proto__ = func.prototype;   }   var  ret = func.apply(res, Array .prototype.slice.call(arguments ,1 ));   if ((typeof  ret === 'object'  || typeof  ret === 'function' ) && ret !== null ){     return  ret;   }   return  res; } var  obj = New(A,1 ,2 );var  obj = new  A(1 ,2 );
实现一个 JSON.stringify 
JSON.stringify(value [,replacer[,space]])
Boolean | Number | String 类型会自动转换成对应的原始值 
undefined、任意函数以及 symbol,会被忽略(出现在非数组对象的属性值中时),或者被转换成 null (出现在数组中时)。 
不可枚举的属性会被忽略 
如果一个对象的属性值通过某种间接的方式指回该对象本身,即循环引用,属性也会被忽略。 
 
 
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 function  jsonStringify (obj )  let  type = typeof  obj;   if (type !== 'object' ){     if (/string|undefined|function/ .test(type)){       obj = '""'  + obj + '""'      }     return  String (obj);   }else {     let  json = [];     let  arr = Array .isArray(obj);     for (let  k in  obj){       let  v = obj[k];       let  type = typeof  v;       if (/string|undefined|function/ .test(type)){         v = '""'  + v + '""' ;       }else  if (type === "object" ){         v = jsonStringify(v);       }       json.push((arr ? ""  : '"'  + k + '":' ) + String (v));       return  (arr ? "[" :"{" ) + String (json) + (arr ? "]"  : "}" )      }   } } jsonStringify({ x  : 5 })  jsonStringify([ 1 , "false"  , false ])  jsonStringify({ b  : undefined })  
实现一个 JSON.parse 
JSON.parse(text[,reviver])
 
eval 1 2 3 4 5 6 7 8 9 10 11 12 13 14 function  jsonParse (json )  var  rx_one = /^[\],:{}\s]*$/ ;   var  rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/ ;   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,"" ))   ){     return  eval ("(" +json+")" );   } } jsonParse(JSON .stringify({ x  : 5 }));  jsonParse(JSON .stringify([ 1 ,'false' ,false ]))  jsonParse(JSON .stringify({ b  : undefined }))  
Function 1 2 var  jsonStr = '{ "age": "20", "name": "jack"}' var  json = (new  Function ('return'  + jsonStr ))();
实现一个 call 或 apply Function.call call 核心:
将函数设为对象的属性 
执行&删除这个函数 
指定 this 到函数并传入给定参数执行函数 
如果不传入参数,默认指向为 window 
 
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 var  foo = {  value: 1 ,   bar: function (     console .log(this .value)   } } foo.bar()  Function .prototype.call2 = function (content = window  )  content.fn = this ;   let  args = [...arguments].slice(1 );   let  result = content.fn(...args);   delete  content.fn;   return  result; } let  foo = {  value: 1 ; } function  bar (name , age )  console .log(name);   console .log(age);   console .log(this .value); } bar.call2(foo,'black'  , '18' );  
Function.apply 的模拟实现
1 2 3 4 5 6 7 8 9 10 11 12 Function .prototype.apply2 = function  (context = window  )  context.fn = this    let  result;      if (arguments [1 ]){     result = context.fn(...arguments[1 ])   }else {     result = context.fn()   }   delete  content.fn;   return  result; } 
实现一个 Function.bind() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Function .prototype.bind2 = function ( content  )  if (typeof  this  != 'function' ){     throw  Error ("not a function" )   }   let  fn = this ;   let  args = [...arguments].slice(1 );   let  resFn = function (     return  fn.apply(this  instanceof  resFn ? this  : content,args.concat(...arguments))   }   function  tmp (   tmp.prototype = this .prototype;   resFn.prototype = new  tmp();   return  resFn; } 
实现一个继承 核心实现是:用一个 F 空的构造函数取代执行了 Parent 这个构造函数
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 function  Parent (name )   this .name = name; } Parent.prototype.sayName = function (   console .log('parent name' , this .name); } function  Child (name,parentName )  Parent.call(this ,parentName);   this .name = name; } function  create (proto )   function  F (   F.prototype = proto;   return  new  F(); } Child.prototype = create(Parent.prototype); Child.prototype = sayName = function (   console .log('child name'  , this .name); } Child.prototype.constructor = Child; var  parent = new  Parent('father' );parent.sayName(); var  child = new  Child('son' ,'father' );
实现一个JS函数柯里化 函数柯里化的主要作用和特点就是参数复用、提前返回和延迟执行。
通用版 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 function  curry (fn , args )   var  length = fn.length;   var  args = args || [];   return  function (     newArgs = args.concat(Array .prototype.slice.call(arguments ));     if (newArgs.length < length){       return  curry.call(this ,fn,newArgs);     }else {       return  fn.apply(this ,newArgs);     }   } } function  multiFn (a, b, c )  return  a * b * c; } var  multi = curry(multiFn);multi(2 )(3 )(4 ); multi(2 ,3 ,4 ); multi(2 )(3 ,4 ); multi(2 ,3 )(4 ); 
ES6 1 2 3 4 5 6 7 8 const  curry = (fn,arr = [] ) =>  (...args ) =>  (  arg => arg.length === fn.length ? fn(...arg) : curry(fn,arg) )([...arr,...args]) let  curryTest = curry((a,b,c,d ) =>  a + b + c + d)curryTest(1 ,2 ,3 )(4 ) curryTest(1 ,2 )(4 )(3 ) curryTest(1 ,2 )(3 ,4 ) 
手写一个Promise(中高级必考) Promise/A+ 规范:
三种状态 pending|fulfilled(resolved)|rejected 
当处于 pending 状态的时候,可以转移到 fulfilled(resolved) 或者 rejected 状态 
当处于 fulfilled( resolved ) 状态或者 rejected 状态的时候,就不可变 
必须有一个 then 异步执行方法, then 接受两个参数且必须返回一个 promise 
 
面试够用版 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 function  myPromise (constructor )  let  self = this ;   self.status = "pending"     self.value = undefined ;    self.reason = undefined ;    function  resolve (value )          if (self.status === 'pending' ){       self.value = value;       self.status = 'resolved' ;     }   }   function  reject (reason )          if (self.status === 'pending' ){       self.reason = reason;       self.status = "rejected" ;     }   }      try {     constructor (resolve,reject );   }catch (e){     reject(e)   } } myPromise.prototype.then = function (onFullfilled,onRejected )   let  self = this ;   switch (self.status){     case  "resolved" :       onFullfilled(self.value);       break ;     case  "rejected" :       onRejected(self.reason)       break ;     default :   } } 
大厂专供版 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 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 const  PENDING = 'pending' ;const  FULFILLED = 'fulfilled' ;const  REJECTED = 'rejected' ;function  Promise (excutor )   let  that = this ;    that.status = PENDING;    that.value = undefined ;    that.reason = undefined ;    that.onFulfilledCallbacks = [];    that.onRejectedCallbacks = [];    function  resolve (value )     if (value instanceof  Promise ){       return  value.then(resolve,reject)     }          setTimeout (()=> {              if (that.status === PENDING){                  that.status = FULFILLED;         that.value = value;         that.onFulfilledCallbacks.forEach(cb = > cb(that.value));       }     });   }   function  reject (reason )      setTimeout (() =>  {              if (that.status === PENDING ){         that.status = REJECTED         that.reason = reason;         that.onRejectedCallbacks.forEach(cb = > cb(that.reason));       }     });   }      try {     excutor(resolve,reject);   }catch (e){     reject(e);   } } Promise .prototype.then = function (onFulfilled,onRejected )  const  that = this ;   let  newPromise;      onFulfilled = typeof  onFulfilled === "function"  ? onFulfilled : value  =>   onRejected = typeof  onRejected === "function"  ? onRejected : reason  =>throw  reason;}   if (that.status === FULFILLED){      return  newPromise = new  Promise ((resolve,reject ) =>  {       setTimeout (()=> {         try {           let  x = onFulfilled(that.value);           resolvePromise(newPromise,x,resolve,reject)         }catch (e){           reject(e);         }       })     })   } } if (that.status === REJECTED){  return  newPromise = new  Promise ((resolve,reject ) => {     setTimeout (() =>  {       try {         let  x = onFulfilled(that.value);         resolvePromise(newPromise,x,resolve,reject)       }catch {         reject(e);       }     })   }) } if (that.status === PENDING){   return  newPromise = new  Promise ((resolve,reject ) =>  {     that.onFulfilledCallback.push((value ) =>  {       try {         let  x = onFulfilled(that.value);         resolvePromise(newPromise,x,resolve,reject)       }catch {         reject(e);       }     })     that.onRejectedCallbacks.push((reason ) =>  {       try {         let  x = onFulfilled(that.value);         resolvePromise(newPromise,x,resolve,reject)       }catch {         reject(e);       }     })   }) } 
手写防抖(Debouncing)和节流(Throttling) 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 function  debounce (fn,wait=50 ,immediate )  let  timer;   return  function (     if (immediate){       fn.apply(this ,arguments );     }     if (timer) clearTimeout (timer);     timer = setTimeout (()=> {       fn.apply(this ,arguments )     },wait)   } } function  throttle (fn,wait )  let  prev = new  Date ();   return  function (     const  args = arguments ;     const  now = new  Date ();     if (now - pre > wait){       fn.apply(this ,args);       prev = new  Date ();     }   } } const  throttle = function (fn, delay, isDebounce )  let  timer   let  lastCall = 0    return  function  (...args )     if ( isDebounce ){       if ( timer ) clearTimeout (timer)       timer = setTimeout  (() => {         fn (...args)       }, delay)     }else {       const  now = new  Date ().getTime()       if (now - lastCall < delay) return        lastCall = now       fn(...args)     }   } } 
手写一个JS深拷贝 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 var  newObj = JSON .parse( JSON .stringify(someObj));function  deepCopy (obj )     if (typeof  obj == "object" ){     var  result = obj.constructor == Array  ? [] : {};     for  ( let  i in  obj ){       result [i] = typeof  obj [i] == "object"  ? deepCopy (obj [i]) :obj [i];}   }else {          var  result = obj;   }   return  result; } 
实现一个instanceOf 1 2 3 4 5 6 7 8 9 function  实现一个instanceOf (left,right )  let  proto = left.__proto__;   let  prototype = right.prototype   while (true ){     if (proto === null ) return  false      if (proto === prototype) return  true      proto = proto.__proto__;   } } 
相关资料
高频前端开发面试问题 √ 
「中高级前端面试」JavaScript手写代码无敌秘籍 √ 25 个最基本的 JavaScript 面试问题及答案(上) √ 
前端面试高频手写代码题 25 个最基本的 JavaScript 面试问题及答案(下) 58道Vue常见面试题集锦,涵盖入门到精通,自测 Vue 掌握程度 由浅入深,66条JavaScript面试知识点和答案解析 2 年前端面试心路历程(字节跳动、YY、虎牙、BIGO) 蚂蚁、字节、滴滴面试经历总结(都已过) 45道JS能力测评经典题总结 
07—47道基础的VueJS面试题(附答案) 
一文帮你搞定 90% 的 JS 手写题!面试手写题不慌了 (qq.com)