不知道是什么东西
new操作符的实现原理
- 首先创建了一个新的
空对象
- 设置原型,将对象的原型设置为函数的
prototype
对象。 - 让函数的
this
指向这个对象,执行构造函数
的代码(为这个新对象添加属性) - 判断函数的返回值类型,如果是
值类型
,返回创建的对象。如果是引用类型
,就返回这个引用类型的对象。
function objectFactory() { let newObject = null; let constructor = Array.prototype.shift.call(arguments); let result = null; // 判断参数是否是一个函数 if (typeof constructor !== "function") { console.error("type error"); return; } // 新建一个空对象,对象的原型为构造函数的 prototype 对象 newObject = Object.create(constructor.prototype); // 将 this 指向新建对象,并执行函数 result = constructor.apply(newObject, arguments); // 判断返回对象 let flag = result && (typeof result === "object" || typeof result === "function"); // 判断返回结果 return flag ? result : newObject; } // 使用方法 objectFactory(构造函数, 初始化参数);
map和weakMap的区别???
JavaScript 类数组对象的定义?
一个拥有 length
属性和若干索引属性的对象就可以被称为类数组对象,类数组对象和数组类似,但是不能调用
数组的方法。常见的类数组对象有 arguments
和 DOM
方法的返回结果。
arguments
是一个对象,它的属性是从 0 开始依次递增的数字,还有length等属性,与数组相似;但是它却没有数组常见的方法属性,如forEach, reduce等,所以叫它们类数组。
类数组转换为数组的方法:
- Array.from(arrayLike);
- Array.prototype.slice.call(arrayLike);
- Array.prototype.splice.call(arrayLike, 0);
- Array.prototype.concat.apply([], arrayLike);
遍历类数组的方法:
// 可以使用call和apply方法 function foo(){ Array.prototype.forEach.call(arguments, a => console.log(a)) } // Array.from方法将类数组转化成数组 function foo(){ const arrArgs = Array.from(arguments) arrArgs.forEach(a => console.log(a)) } // 展开运算符将类数组转化成数组 function foo(){ const arrArgs = [...arguments] arrArgs.forEach(a => console.log(a)) }
数组的原生:
- 转换为
字符串
:toString()、toLocalString()、join() - 数组
尾部
操作的方法 pop() 和 push()。 - 数组
首部
操作的方法 shift() 和 unshift() - 数组
连接
的方法 concat() ,返回的是拼接好的数组,不影响
原数组。 - 数组
截取
办法 slice(),用于截取数组中的一部分返回,不影响
原数组。 - 数组
插入
方法 splice(),影响原数组查找特定项的索引的方法 - indexOf() 和 lastIndexOf()
迭代
方法 every()、some()、filter()、map() 和 forEach() 方法- 数组
归并
方法 reduce()
什么是 DOM 和 BOM?
DOM: 指的是文档对象模型
,是把文档当做一个对象,这个对象主要定义了处理网页内容
的方法和接口。
BOM: 指的是浏览器对象
模型,是把浏览器当做一个对象来对待,这个对象主要定义了与浏览器进行交互的方法和接口。BOM的核心是 window
,window
对象含有 location
对象、navigator
对象、screen
对象等子对象,并且 DOM
的最根本的对象 document
对象也是 BOM 的 window
对象的子对象
。
escape、encodeURI、encodeURIComponent 的区别
encodeURI: 是对整个 URI
进行转义,将 URI
中的非法字符转换
为合法字符
,所以对于一些在 URI 中有特殊意义的字符不会进行转义。
encodeURIComponent: 是对 URI 的组成部分进行转义,所以一些特殊字符也会得到转义。
escape 和 encodeURI: 的作用相同,不过它们对于 unicode
编码为 0xff 之外字符的时候会有区别,escape
是直接在字符的 unicode
编码前加上 %u,而 encodeURI
首先会将字符转换为 UTF-8
的格式,再在每个字节前加上 %。
AJAX的理解,实现一个AJAX请求
AJAX
指的是通过 JavaScript 的 异步通信
,从服务器获取 XML 文档从中提取数据,再更新当前网页的对应部分,而不用刷新整个
网页。
创建AJAX请求的步骤:
- 创建一个
XMLHttpRequest
对象。 - 在这个对象上使用
open
方法创建一个HTTP
请求 - 添加
请求头
和状态监听
方法 ,XMLHttpRequest
对象一共有5
个状态,当它的状态变化时会触发onreadystatechange
事件 send
请求
const SERVER_URL = "/server"; let xhr = new XMLHttpRequest(); // 创建 Http 请求 xhr.open("GET", url, true); // 设置状态监听函数 xhr.onreadystatechange = function() { if (this.readyState !== 4) return; // 当请求成功时 if (this.status === 200) { handle(this.response); } else { console.error(this.statusText); } }; // 设置请求失败时的监听函数 xhr.onerror = function() { console.error(this.statusText); }; // 设置请求头信息 xhr.responseType = "json"; xhr.setRequestHeader("Accept", "application/json"); // 发送 Http 请求 xhr.send(null);
使用Promise封装AJAX:
// promise 封装实现: function getJSON(url) { // 创建一个 promise 对象 let promise = new Promise(function(resolve, reject) { let xhr = new XMLHttpRequest(); // 新建一个 http 请求 xhr.open("GET", url, true); // 设置状态的监听函数 xhr.onreadystatechange = function() { if (this.readyState !== 4) return; // 当请求成功或失败时,改变 promise 的状态 if (this.status === 200) { resolve(this.response); } else { reject(new Error(this.statusText)); } }; // 设置错误监听函数 xhr.onerror = function() { reject(new Error(this.statusText)); }; // 设置响应的数据类型 xhr.responseType = "json"; // 设置请求头信息 xhr.setRequestHeader("Accept", "application/json"); // 发送 http 请求 xhr.send(null); }); return promise; }
JavaScript为什么要进行变量提升,有什么问题?
变量提升
的表现是,无论在函数中何处位置声明的变量,好像都被提升到了函数的首部
,可以在变量声明前访问到而不会报错。
造成变量声明提升的本质原因
是 js 引擎
在代码执行前
有一个解析
的过程,创建了执行上下文,初始化了一些代码执行时需要用到的对象。当访问一个变量
时,会到当前执行上下文
中的作用域链
中去查找
首先要知道,JS在拿到一个变量或者一个函数的时候,会有两步操作,即解析
和执行
。
解析: JS会检查语法,并对函数进行预编译。解析的时候会先创建一个全局执行上下文环境,先把代码中即将执行的变量、函数声明都拿出来,变量先赋值为undefined,函数先声明好可使用。在一个函数执行之前,也会创建一个函数执行上下文环境,跟全局执行上下文类似,不过函数执行上下文会多出this、arguments和函数的参数。
执行: 就是按照代码的顺序依次执行。
变量提升
的两个原因
:提高性能
容错性更好
ES6模块与CommonJS模块有什么异同?
CommonJS
是社区规范, ES6 Module
是官方后出的规范
CommonJS
模块输出的是一个值的拷贝,ES6 Module
输出的是值的引用CommonJS
模块是运行时加载,ES6 Module
是编译时输出接口。CommonJS
是单个值导出,ES6 Module
可以导出多个CommonJS
是动态语法可以写在判断里,ES6 Module
静态语法只能写在顶层CommonJS
的 this 是当前模块,ES6 Module
的 this 是 undefined
React的 setState 同步还是异步
setState
本身代码的执行肯定是同步的,这里的异步是指是多个 state 会合成到一起进行批量更新。 同步还是异步取决于它被调用的环境。
- 如果
setState
在 React 能够控制的范围被调用,它就是异步的。比如合成事件处理函数,生命周期函数, 此时会进行批量更新,也就是将状态合并后再进行 DOM 更新。 - 如果
setState
在原生 JavaScript 控制的范围被调用,它就是同步的。比如原生事件处理函数,定时器回调函数,Ajax 回调函数中,此时setState
被调用后会立即更新 DOM 。
对函数式编程的理解
函数式编程有两个核心概念
- 数据不可变(无副作用): 它要求你所有的数据都是不可变的,这意味着如果你想修改一个对象,那你应该创建一个新的对象用来修改,而不是修改已有的对象。
- 无状态: 主要是强调对于一个函数,不管你何时运行,它都应该像第一次运行一样,给定相同的输入,给出相同的输出,完全不依赖外部状态的变化。
纯函数带来的意义
- 便于测试和优化:这个意义在实际项目开发中意义非常大,由于纯函数对于相同的输入永远会返回相同的结果,因此我们可以轻松断言函数的执行结果,同时也可以保证函数的优化不会影响其他代码的执行。
- 可缓存性:因为相同的输入总是可以返回相同的输出,因此,我们可以提前缓存函数的执行结果。
- 更少的 Bug:使用纯函数意味着你的函数中不存在指向不明的 this,不存在对全局变量的引用,不存在对参数的修改,这些共享状态往往是绝大多数 bug 的源头。