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 ($ ) { } )(jQuery);
在 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;
检测 isNaN Number.isNaN()
下列代码将输出什么?并解释原因 console.log(0.1 + 0.2); console.log(0.1 + 0.2 == 0.3);
JavaScript中的数字和浮点精度的处理相同,因此,可能不会总是产生预期的结果。
9.讨论写函数 isInteger(x) 的可能方法,用于确定x是否是整数
在ECMAScript规格说明中,整数只概念上存在:即,数字值总是存储为浮点值。
function isInteger(x) { return (x^0) === x; } function isInteger(x) { return Math.round(x) === x; } function isInteger(x) { return (typeof x === ‘number’) && (x % 1 === 0); function isInteger(x) { return parseInt(x, 10) === x; } // 目变得足够大,为指数形式(例如, 1e+21)
Math.ceil() 和 Math.floor() 在上面的实现中等同于 Math.round()。
下列代码行1-4如何排序,使之能够在执行代码时输出到控制台? 为什么 (function() { console.log(1); setTimeout(function(){console.log(2)}, 1000); setTimeout(function(){console.log(3)}, 0); console.log(4); })();
output 1 4 3 2
浏览器有一个事件循环,会检查事件队列和处理未完成的事件。例如,如果时间发生在后台(例如,脚本的 onload 事件)时,浏览器正忙(例如,处理一个 onclick),那么事件会添加到队列中。当onclick处理程序完成后,检查队列,然后处理该事件(例如,执行 onload 脚本)。 同样的, setTimeout() 也会把其引用的函数的执行放到事件队列中,如果浏览器正忙的话。 当setTimeout()的第二个参数为0的时候,它的意思是“尽快”执行指定的函数。具体而言,函数的执行会放置在事件队列的下一个计时器开始。但是请注意,这不是立即执行:函数不会被执行除非下一个计时器开始。这就是为什么在上述的例子中,调用 console.log(4) 发生在调用 console.log(3) 之前(因为调用 console.log(3) 是通过setTimeout被调用的,因此会稍微延迟)。
写一个简单的函数(少于80个字符),要求返回一个布尔值指明字符串是否为回文结构 function isPalindrome(str) { str = str.replace(/W/g, ‘’).toLowerCase(); return (str == str.split(‘’).reverse().join(‘’)); }
console.log(isPalindrome(“level”)); // logs ‘true’ console.log(isPalindrome(“levels”)); // logs ‘false’ console.log(isPalindrome(“A car, a man, a maraca”)); // logs ‘true’
写一个 sum方法,在使用下面任一语法调用时,都可以正常工作 console.log(sum(2,3)); // Outputs 5 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方法的区别是()。 答案: get是从服务器上获取数据,post是向服务器传送数据。 get是把参数数据队列加到提交表单的ACTION属性所指的URL中,值和表单内各个字段一一对应,在URL中可以看到。post是通过HTTPpost机制,将表单内各个字段与其内容放置在HTML HEADER内一起传送到ACTION属性所指的URL地址。用户看不到这个过程。 对于get方式,服务器端用Request.QueryString获取变量的值,对于post方式,服务器端用Request.Form获取提交的数据。 get传送的数据量较小,不能大于2KB。post传送的数据量较大,一般被默认为不受限制。但理论上,IIS4中最大量为80KB,IIS5中为100KB。(这里有看到其他文章介绍get和post的传送数据大小跟各个浏览器、操作系统以及服务器的限制有关) get安全性非常低,post安全性较高。
简述浏览器发起一个网络请求(HTTP请求事务)后,都经历了哪些步骤: 输入网址:输入url地址 域名解析:浏览器按照顺序解析,自身的dns缓存——客户端自身的dns缓存——本地host文件——路由器缓存 建立连接:浏览器获得域名对应的ip地址后,发起tcp三次握手,将客户端与服务端建立连接(http基于tcp协议,tcp为传输层协议) 返回数据:服务端接收请求并将数据返回给浏览器 处理数据:浏览器拿到返回资源后进行客户端渲染,将完整页面呈现给用户。
请为所有数组对象添加一个通用的 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 => 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)