原型、原型链、构造函数、实例、继承
constructor 构造函数:
构造函数一般为了区别普通函数要求首字母大写
prototype 原型
原型指的就是一个对象,实例“继承”那个对象的属性。在原型上定义的属性,通过“继承”,实例也拥有了这个属性。“继承”这个行为是在new操作符内部实现的。
有几种方式可以实现继承
用原型实现继承有什么缺点,怎么解决
arguments
arguments对象不是一个Array,它类似于 Array,但除了length属性和索引元素之外没有任何Array属性。例如,它没有 pop 方法,但是它可以被转换为一个真正的Array。
数据类型判断
数据类型
基本类型
String、Number、Boolean、Symbol、Undefined、Null
引用类型
Object、Function、Array
判断 JS 数据类型的几种方法
1、typeof
typeof 是一个操作符,其右侧跟一个一元表达式,并返回这个表达式的数据类型。返回的结果用该类型的字符串(全小写字母)形式表示,包括以下 7 种:number、boolean、symbol、string、object、undefined、function 等。 对于 null ,返回 object 类型。 对于 function 返回 function 类型。
2、instanceof
instanceof 是用来判断 A 是否为 B 的实例,表达式为:A instanceof B,如果 A 是 B 的实例,则返回 true,否则返回 false。 instanceof 检测的是原型。
3、toString
toString() 是 Object 的原型方法,调用该方法,默认返回当前对象的 [[Class]] 。这是一个内部属性,其格式为 [object Xxx] ,其中 Xxx 就是对象的类型。 2.对于 Object 对象,直接调用 toString() 就能返回 [object Object] 。而对于其他对象,则需要通过 call / apply 来调用才能返回正确的 类型信息。
Object.prototype.toString.call('') ; // [object String]
Object.prototype.toString.call(1) ; // [object Number]
Object.prototype.toString.call(true) ; // [object Boolean]
Object.prototype.toString.call(Symbol()); //[object Symbol]
Object.prototype.toString.call(undefined) ; // [object Undefined]
作用域、作用域链、闭包
闭包
本质
存在对父级作用域的引用,闭包就是将函数内部和函数外部连接起来的一座桥梁。
用途
1.可以读取函数内部的变量 2.让这些变量的值始终保持在内存中
注意
会造成内存泄漏(有一块内存空间被长期占用,而不被释放) 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在 IE 中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
垃圾回收机制
当一个值,在内存中失去引用时,垃圾回收机制会根据特算法找到它,并将其回收,释放内存 函数的执行上下文,在执行完毕之后,生命周期结束,那么该函数的执行上下文就会失去引用。其占用的内存空间很快就会被垃圾回收器释放。可是闭包的存在,会阻止这一过程。
浅拷贝、深拷贝
浅拷贝
浅拷贝只在根属性上在堆内存中创建了一个新的的对象,复制了基本类型的值,但是复杂数据类型也就是对象则是拷贝相同的地址
Object.assign()、扩展运算符、Array.prototype.slice()
语法:Object.assign(target, ...sources)
扩展运算符语法:var cloneObj = { ...obj };
语法: arr.slice(begin, end);
深拷贝
将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象
JSON.stringify()是目前前端开发过程中最常用的深拷贝方式
原理是把一个对象序列化成为一个 JSON 字符串,将对象的内容转换成字符串的形式再保存在磁盘上
再用 JSON.parse()反序列化将 JSON 字符串变成一个新的对象
var obj1 = {
a:1,
b:[1,2,3]
}
var str = JSON.stringify(obj1)
var obj2 = JSON.parse(str)
console.log(obj2); //{a:1,b:[1,2,3]}
obj1.a=2
obj1.b.push(4);
console.log(obj1); //{a:2,b:[1,2,3,4]}
console.log(obj2); //{a:1,b:[1,2,3]}
图片懒加载、预加载
两者的行为是相反的,一个是提前加载,一个是迟缓甚至不加载。
懒加载对服务器前端有一定的缓解压力作用,预载则会增加服务器前端压力。
实现页面加载进度条
this指向
1、在对象的方法中使用,this 指向当前的对象;
2、在独立的函数中使用
在严格模式下,this 指向 undefined,非严格模式下,this 指向全局对象,比如 window;
3、通过 call\apply\bind 来指定
三者都可传入一个要改变的 this 的值,来改变 this 指向,区别就是 call\apply 改变的同时执行函数,bind 不执行,而是返回这个函数; call\apply 第一个参数就是要改变的 this 的值,区别就是 call 传入的是参数列表,apply 传入的是参数数组
4、构造函数
如果一个函数是构造函数,那么 this 就指向它实例化出来的对象
5、箭头函数
箭头函数不会创建自己的 this,它只会从自己的作用域链的上一层继承 this,另外箭头函数里也不能使用 call\apply\bind 修改 this 的指向
函数式编程
函数式编程中的函数指的并不是编程语言中的函数(或方法),它指的是数学意义上的函数,即映射关系(如:y = f(x)),就是 y 和 x 的对应关系。
高阶函数
在函数式编程中,如果函数能满足下面任一要求就可以被称为高阶函数(higher-order function):
3.1 接受至少一个函数作为参数
3.2 返回的结果是一个函数
为什么会有同源策略
同源
同源即 同协议,端口,域名
同源策略的限制
不能获取不同源的 cookie,LocalStorage 和 indexDB
不能获取非同源的 DOM
不能发送非同源的 ajax 请求。(准确说应该是可以向非同源的服务器发起请求,但是返回的数据会被浏览器拦截)
同源策略由浏览器执行
事件模型
事件委托、事件代理
for...in和for..of区别
1.for in遍历的是数组的索引(即键名),而for of遍历的是数组元素值。
2.for-in总是得到对象的key或数组、字符串的下标。
3.for-of总是得到对象的value或数组、字符串的值,另外还可以用于遍历Map和Set。
4.for...of不能遍历对象,需要结合Object.keys()使用
async/await
立即执行函数和使用场景
不必为函数命名,避免了污染全局变量
立即执行函数内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量
封装变量
设计模式(要求说出如何实现,应用,优缺点)/单例模式实现
iframe 的缺点有哪些
服务端渲染和客户端渲染的区别
SPA:单页面应用:只有一张 Web 页面的应用,是一种从 Web 服务器加载的富客户端,单页面跳转仅刷新局部资源,公共资源仅需加载一次,常用于 PC 端网站,购物等网站。
优点:页面之间切换快,减少了服务器的压力
缺点:首屏打开慢,不利于 SEO 搜索引擎优化,利用搜索引擎的规则是提高网站在有关搜索引擎的自然排名。
两者本质的区别
客户端渲染和服务器端渲染的最重要的区别就是究竟是谁来完成 html 文件的完整拼接,如果是在服务器端完成的,然后返回给客户端,就是服务器端渲染,而如果是前端做了更多的工作完成了 html 的拼接,则就是客户端渲染。
垃圾回收机制
释放垃圾占用的空间,防止内存泄露。有效的使用可以使用的内存,对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收。
垃圾回收机制
user-gold-cdn.xitu.io/2019/10/5/1…
浏览器中相对简单,共有两个事件队列,当主线程空闲时会清空 Microtask queue(微任务队列)依次执行 Task Queue(宏任务队列)中的回调函数,每执行完一个之后再清空 Microtask queue。
“当前执行栈” -> “micro-task” -> “task queue 中取一个回调” -> “micro-task” -> ... (不断消费 task queue) -> “micro-task”
如何判断两个对象相等?
JSON.stringify(obj)==JSON.stringify(obj)执行速度是最快的
slice、splice、split 的区别
slice
slice(start,[end])方法:
该方法是对数组进行部分截取,该方法返回一个新数组,参数start是截取的开始数组索引,end参数等于你要取的最后一个字符的位置值加上1(可选)。
总结:slice的参数如果是正数就从左往右数,如果是负数的话就从右往左边数,截取的数组与数的方向一致,如果是2个参数则截取的是数的交集,没有交集则返回空数组
ps:slice也可以切割字符串,用法和数组一样,但要注意空格也算字符。
splice
splice(start,deletecount,item):移除,splice方法从array中移除一个或多个数组,并用新的item替换它们。
start:起始位置;deletecount:删除位数;item:替换的item;
返回值为被删除的字符串
如果有额外的参数,那么item会插入到被移除元素的位置上。
split(字符串)
split:string.split(separator,limit):split方法把这个string分割成片段来创建一个字符串数组。
总结方法是否改变原数组
改变原数组:push()、pop()、shift()、unshift()、splice()、reverse()、sort()
不改变原数组:toString()、join()、slice()、concat()、every()、some()、indexOf()、lastIndexOf()、forEach()、filter()、find()、reduce()
Set 和 Map 区别
Set 和 Map 主要的应用场景在于 数据重组 和 数据储存 Set 是一种叫做集合的数据结构,Map 是一种叫做字典的数据结构
集合(Set)
ES6 新增的一种新的数据结构,类似于数组,但成员是唯一且无序的,没有重复的值。
Set 本身是一种构造函数,用来生成 Set 数据结构。
向 Set 加入值的时候,不会发生类型转换,所以5和"5"是两个不同的值。
1.Set实例属性
constructor: 构造函数
size:元素数量
2.Set实例方法
操作方法:
add(value):新增,相当于 array里的push
delete(value):存在即删除集合中value
has(value):判断集合中是否存在 value
clear():清空集合
遍历方法(遍历顺序为插入顺序):
keys():返回一个包含集合中所有键的迭代器
values():返回一个包含集合中所有值得迭代器
entries():返回一个包含Set对象中所有元素得键值对迭代器
forEach:用于对集合成员执行callbackFn操作
let set = new Set([1, 2, 3])
set.forEach((value, key) => {
console.log(key + ' : ' + value)
}) //1 : 1 2 : 2 3 : 3
字典(Map)
集合 与 字典 的区别:
共同点:集合、字典 可以储存不重复的值
不同点:集合 是以 [value, value]的形式储存元素,字典 是以 [key, value] 的形式储存 1.Map的属性
constructor:构造函数
size:返回字典中所包含的元素个数
2.Set实例方法
操作方法:
set(key, value):向字典中添加新元素
get(key):通过键查找特定的数值并返回
has(key):判断字典中是否存在键key
delete(key):通过键 key 从字典中移除对应的数据
clear():将这个字典中的所有元素删除
遍历方法
Keys():将字典中包含的所有键名以迭代器形式返回
values():将字典中包含的所有数值以迭代器形式返回
entries():返回所有成员的迭代器
forEach():遍历字典的所有成员
总结
1、Set
成员唯一、无序且不重复
[value, value],键值与键名是一致的(或者说只有键值,没有键名)
可以遍历,方法有:add、delete、has
2、Map
本质上是键值对的集合,类似集合
可以遍历,方法很多可以跟各种数据格式转换
Object 结构提供了 “字符串-值” 对应,Map则提供了 “值-值” 的对应
ES5/ES6 的继承除了写法以外还有什么区别?
async/await
new的作用及实现一个new
1、创建一个新对象obj
2、把obj的__proto__指向Dog.prototype 实现继承
3、执行构造函数,传递参数,改变this指向 Dog.call(obj, ...args)
4、最后把obj赋值给实例返回(如果构造函数返回一个非基本类型的值,则返回这个值,否则上面创建的对象)
function _new(fn, ...arg) {
const obj = Object.create(fn.prototype);
const ret = fn.apply(obj, arg);
return ret instanceof Object ? ret : obj;
}