js

38 阅读11分钟

原型、原型链、构造函数、实例、继承

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():遍历字典的所有成员

总结

1Set
成员唯一、无序且不重复
[value, value],键值与键名是一致的(或者说只有键值,没有键名)
可以遍历,方法有:add、delete、has
2Map
本质上是键值对的集合,类似集合
可以遍历,方法很多可以跟各种数据格式转换
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;
}