JS基础
new的原理
- new做了哪些事
- new返回不同的类型会有什么表现
- 手写new的实现过程
执行一个构造函数,返回一个实例对象
- 创建一个新对象
- 使得新对象的__proto__ 指向构造函数的prototype
- 执行构造函数,通过call apply 改变this的指向
- 而后判断确保返回的是一个对象。否则返回新对象,而不是new步骤生成的this对象
其中有两种分支情况
不使用new的情况 构造函数中包含return 语句的
new关键词执行之后总会返回一个对象
手撕
function create(fn,...args){
var obj = Object.create(fn.prototype)
var res = fn.apply(fn,args)
return typeof res === 'object' ? res : obj
}
原型/原型链
__proto__和prototype关系:__proto__和constructor是对象独有的 ,prototype属性是函数独有的
当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么它就会去它的原型对象里找这个属性,这个原型对象又会有自己的原型,于是就这样一直找下去,也就是原型链的概念。
-
原型(prototype):用于实现对象的属性继承。每个JavaScript对象中都包含一个__proto__(非标准)的属性指向它爹(该对象的原型),可obj.__proto__进行访问。
-
构造函数: 通过new新建一个对象的函数
-
实例: 通过构造函数和new创建出来的对象,即为实例 实例通过__proto__ 指向原型,通过constructor指向构造函数
原型链
原型链是由原型对象组成,每个对象都有 proto 属性,指向了创建该对象的构造函数的原型,proto 将对象连接起来组成了原型链。是一个用来实现继承和共享属性的有限的对象链
属性查找机制: 若这一层查找不到该属性时,则沿着原型链向上查找
属性修改机制: 只会修改实例对象本身的属性,如果不存在,则进行添加该属性,如果需要修改原型的属性时,则可以用: b.prototype.x = 2;但是这样会造成所有继承于该对象的实例的属性发生改变。
每个对象都有 proto 属性,指向了创建该对象的构造函数的原型。其实这个属性指向了 [[prototype]],但是 [[prototype]]是内部属性,我们并不能访问到,所以使用 _proto_来访问。
继承
class为语法糖 ,JS中并不存在类
Person instanceof Function
需要记住的实际上就两个把
- class继承
class Parent {
constructor(value) {
this.val = value
}
getValue() {
console.log(this.val)
}
}
class Child extends Parent {
constructor(value) {
super(value)
this.val = value
}
}
let child = new Child(1)
child.getValue() // 1
child instanceof Parent // true
class 继承的核心就是 extends表明继承自哪个父类。并且在constructor构造目录中必须调用super()
相当于先借用父类完成子类this的构造,再向子类中添加this的相关属性
- 组合继承的最推荐姿势
function Parent5(){
this.name = 'name'
this.way = 'father'
}
function Child5(){
Parent5.call(this)
this.age = '16'
}
Child5.prototype = Object.create(Parent5.prototype)
Child5.prototype.constructor = Child5
继承
事件机制
- 事件捕获阶段 有外向内 window document body
- 处于目标阶段
- 事件冒泡阶段 由内向外
W3C的标准是先捕获再冒泡, addEventListener的第三个参数决定把事件注册在捕获(true)还是冒泡(false)
- event.preventDefault():取消事件对象的默认动作以及继续传播。 event.stopPropagation()/ event.cancelBubble = true:阻止事件冒泡。
重点:
- 通常我们使用 addEventListener 注册事件,该函数的第三个参数可以是布尔值,也可以是对象。对于布尔值 useCapture 参数来说,该参数默认值为 false。useCapture 决定了注册的事件是捕获事件还是冒泡事件
- 一般来说,我们只希望事件只触发在目标上,这时候可以使用 stopPropagation 来阻止事件的进一步传播。通常我们认为 stopPropagation 是用来阻止事件冒泡的,其实该函数也可以阻止捕获事件。stopImmediatePropagation 同样也能实现阻止事件,但是还能阻止该事件目标执行别的注册事件
Iterator 迭代器
Iterator(迭代器)是一种接口,也可以说是一种规范。为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署Iterator接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。
let arr = [{num:1},2,3]
let it = arr[Symbol.iterator]() // 获取数组中的迭代器
console.log(it.next()) // { value: Object { num: 1 }, done: false }
console.log(it.next()) // { value: 2, done: false }
console.log(it.next()) // { value: 3, done: false }
console.log(it.next()) /
手动给对象布置遍历
let obj = {
id: '123',
name: '张三',
age: 18,
gender: '男',
hobbie: '睡觉'
}
obj[Symbol.iterator] = function(){
let keyarr = Object.keys(obj)
let index = 0
return {
next(){
return index < keyarr.length ? {
value: {
key: keyarr[index],
val: obj[keyarr[index++]]
}
} : {
done: true
}
}
}
}
for (let key of obj) {
console.log(key)
}
Promise 标记待完善 重点 async对象返回的是
这里你谈 promise的时候,除了将他解决的痛点以及常用的 API 之外,最好进行拓展把 eventloop 带进来好好讲一下,microtask(微任务)、macrotask(任务) 的执行顺序,如果看过 promise 源码,最好可以谈一谈 原生 Promise 是如何实现的。Promise 的关键点在于callback 的两个参数,一个是 resovle,一个是 reject。还有就是 Promise 的链式调用(Promise.then(),每一个 then 都是一个责任人)
核心大概是源码,以及promise语句是怎么使用的
这个地方讲的不太清楚
Generator
Generator 是 ES6中新增的语法,和 Promise 一样,都可以用来异步编程。Generator函数可以说是Iterator接口的具体实现方式。Generator 最大的特点就是可以控制函数的执行。
应该会算。看题写答案。因为async await 是这个内容的语法糖。写法更加精简
我也不是太明白给两个例子自己揣摩吧
function *foo(x) {
let y = yield (x + 1)
let z = yield (y / 3)
return (x + y + z)
}
let it = foo(5)
console.log(it.next()) // => {value: 6, done: false}
console.log(it.next(12)) // => {value: 8, done: false}
console.log(it.next(13)) // =>
// { value: 6, done: false }
// { value: 4, done: false }
// { value: 30, done: true }
function *foo(x) {
let y = 2*(yield (x + 1))
let z = yield (y / 3)
return (x + y + z)
}
let it = foo(5)
console.log(it.next()) // => {value: 6, done: false}
console.log(it.next(12)) // => {value: 8, done: false}
console.log(it.next(13)) // =>
// { value: 6, done: false }
// { value: 8, done: false }
// { value: 42, done: true }
async/await
注意宏任务与微任务的关系
事件循环
之前理解的有些问题
宏任务指的是一些特定的代码段,图里面有
- 默认代码从上到下执行,执行环境通过script来执行(宏任务)
- 在代码执行过程中,调用定时器 promise click事件...不会立即执行,需要等待当前代码全部执行完毕
- 给异步方法划分队列,分别存放到微任务(立即存放)和宏任务(时间到了或事情发生了才存放)到队列中
- script执行完毕后,会清空所有的微任务
- 微任务执行完毕后,会渲染页面(不是每次都调用) 再去宏任务队列中看有没有到达时间的,拿出来其中一个执行
- 执行完毕后,按照上述步骤不停的循环
垃圾回收
回收主要用到 标记清楚 , 引用计数
v8 的垃圾回收机制基于分代回收机制,这个机制又基于世代假说,这个假说有两个特点,一是新生的对象容易早死,另一个是不死的对象会活得更久。基于这个假说,v8 引擎将内存分为了新生代和老生代。
深浅拷贝
浅拷贝出现的情况,引用类型的数据。因为保存的是数据的地址,所以复制时出现浅拷贝
- object.assign
let target = {};
let source = { a: { b: 1 } };
Object.assign(target, source);
console.log(target); // { a: { b: 1 } };
-
扩展运算符方式
-
concat 对待数组好像是深拷贝
-
slice 在原数组上更改, 好像是浅拷贝
节流与防抖
- 防抖为避免短时多次点击向后端发送多次请求
- 函数节流为单位事件内重复多次的事件。只有一次能够生效
代码Github 库内都含有
Proxy代理
var f = new Proxy(target,handler)
var f = new Proxy(target,{
get: function(target,key){
},
set: function(target,key,value){
}
})
作用:
- 拦截并监视外部对象对对象的访问
- 在进行复杂的请求前对操作进行校验或者管理或预处理
Ajax
var xhr = window.XMLHttpRequest?new XMLHttpRequest():new ActiveXObject('Microsoft.XMLHTTP');// 兼容IE6及以下版本
//2:配置 Ajax请求地址
xhr.open('get','index.xml',true);
//3:发送请求
xhr.send(null); // 严谨写法
//4:监听请求,接受响应
xhr.onreadysatechange=function(){
if(xhr.readySate==4&&xhr.status==200 || xhr.status==304 )
console.log(xhr.responsetXML)
}
数组
改变自身的方法
基于 ES6,会改变自身值的方法一共有 9 个,分别为 pop、push、reverse、shift、sort、splice、unshift,以及两个 ES6 新增的方法 copyWithin 和 fill
- pop,push 从右边添加删除
- shift unshift shift从左边删除
- sort(func) 里面其实是一个函数,默认的是左小右大
<!-- 倒叙 -->
var points = [40,100,1,5,25,10];
points.sort(function(a,b){return b-a});
-
splice 修改原数组 添加或者删除元素
-
- array.splice(index,howmany,item1,.....,itemX)
-
- index 为下标开始位置 howmany 为操作数目
-
slice 不修改原数组
-
array.slice(start, end)
-
reverse
-
fill array.fill(value, start, end)
-
copyWithin
-
- array.copyWithin(target, start, end)
-
- copyWithin() 方法用于从数组的指定位置拷贝元素到数组的另一个指定位置中。
ES7
- concat join slice toString toLocaleString、indexOf、lastIndexOf、未形成标准的 toSource,以及 ES7 新增的方法 includes
- concat
-
- arr.concat()
- join
-
- 数组转字符串
- toString
-
- 直译 讲数组转化为字符串 以,相分割
- toLocaleString()
- indexof
- string.indexOf(searchvalue,start) 数组也适用
- includes
- 是否包含返回 true false
遍历的方法
forEach、every、some、filter、map、reduce、reduceRight,以及 ES6 新增的方法 entries、find、findIndex、keys、values
forEach
- array.forEach(function(currentValue, index, arr), thisValue)
every
- array.every(function(currentValue,index,arr), thisValue)
- every() 方法用于检测数组所有元素是否都符合指定条件(通过函数提供)。
- 有一个返回false则结束
some
- some() 方法用于检测数组中的元素是否满足指定条件(函数提供)。
filter
- filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。
map
- map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。
reduce
- 就是之前能够处理累加的东西
- array.reduce(function(total, currentValue, currentIndex, arr), initialValue)
reduceRight
- 从右边开始 这一部分根本字如其意 数组和字符串的方法
- img-repo.poetries.top/images/2021…
- poetries1.gitee.io/img-repo/20…
- poetries1.gitee.io/img-repo/20…
entries
var array = ["a", "b", "c"];
var iterator = array.entries();
console.log(iterator.next().value); // [0, "a"]
console.log(iterator.next().value); // [1, "b"]
console.log(iterator.next().value); // [2, "c"]
console.log(iterator.next().value); // undefined, 迭代器处于数组末尾时, 再迭代就会返回undefined
/
find
- find() 方法返回通过测试(函数内判断)的数组的第一个元素的值。
- array.find(function(currentValue, index, arr),thisValue)
findIndex
- findIndex() 方法返回通过测试(函数内判断)的数组的第一个元素的下标。
keys() 方法用于从数组创建一个包含数组键的可迭代对象。
values() 值
理解JS类数组
函数里面的参数对象 arguments; 用 getElementsByTagName/ClassName/Name 获得的 HTMLCollection 用 querySelector 获得的 NodeList
- arguments 是函数中传递的参数值的集合。因为它含有length属性,但它毕竟不是数组。没有数组中的内置方法,如forEach,reduce,filter和map
Array.prototype.slice.call(arguments);
Object.prototype.toString.call(arguments)
HTMLCollection
HTMLCollection 简单来说是 HTML DOM 对象的一个接口,这个接口包含了获取到的 DOM 元素集合,返回的类型是类数组对象,如果用 typeof 来判断的话,它返回的是 'object'。它是及时更新的,当文档中的 DOM 变化时,它也会随之变化。
NodeList
类数组对象,可以通过下标进行遍历,传递参数使用 img-repo.poetries.top/images/2021… callee属性指向自己
类数组转化为数组的方法
function sum(a, b) {
let args = Array.prototype.slice.call(arguments);
// let args = [].slice.call(arguments); // 这样写也是一样效果
console.log(args.reduce((sum, cur) => sum + cur));
}
Array.prototype.slice.call(arguments);
Array.prototype.concat.call([],arguments);
// ES6 解构赋值
function sum(a, b) {
let args = Array.from(arguments);
console.log(args.reduce((sum, cur) => sum + cur));
}
sum(1, 2); // 3
function sum(a, b) {
let args = [...arguments];
console.log(args.reduce((sum, cur) => sum + cur));
}
sum(1, 2); // 3
function sum(...args) {
console.log(args.reduce((sum, cur) => sum + cur));
}
sum(1, 2); //
数组扁平化
- 递归
- reduce
- ...
- flat arr.flat([depth]) 默认为1 depth表示可展开数组的深度。 Infinity 无限深度
通过这个在线网站 regexper.com/ 正则分析成容易理解的可视化的逻辑脑图