关于javascript:2023我的前端面试小结

2次阅读

共计 15135 个字符,预计需要花费 38 分钟才能阅读完成。

对事件委托的了解

(1)事件委托的概念

事件委托实质上是利用了 浏览器事件冒泡 的机制。因为事件在冒泡过程中会上传到父节点,父节点能够通过事件对象获取到指标节点,因而能够把子节点的监听函数定义在父节点上,由父节点的监听函数对立解决多个子元素的事件,这种形式称为事件委托(事件代理)。

应用事件委托能够不必要为每一个子元素都绑定一个监听事件,这样缩小了内存上的耗费。并且应用事件代理还能够实现事件的动静绑定,比如说新增了一个子节点,并不需要独自地为它增加一个监听事件,它绑定的事件会交给父元素中的监听函数来解决。

(2)事件委托的特点

  • 缩小内存耗费

如果有一个列表,列表之中有大量的列表项,须要在点击列表项的时候响应一个事件:

<ul id="list">
  <li>item 1</li>
  <li>item 2</li>
  <li>item 3</li>
  ......
  <li>item n</li>
</ul>

如果给每个列表项一一都绑定一个函数,那对于内存耗费是十分大的,效率上须要耗费很多性能。因而,比拟好的办法就是把这个点击事件绑定到他的父层,也就是 ul 上,而后在执行事件时再去匹配判断指标元素,所以事件委托能够缩小大量的内存耗费,节约效率。

  • 动静绑定事件

给上述的例子中每个列表项都绑定事件,在很多时候,须要通过 AJAX 或者用户操作动静的减少或者去除列表项元素,那么在每一次扭转的时候都须要从新给新增的元素绑定事件,给行将删去的元素解绑事件;如果用了事件委托就没有这种麻烦了,因为事件是绑定在父层的,和指标元素的增减是没有关系的,执行到指标元素是在真正响应执行事件函数的过程中去匹配的,所以应用事件在动静绑定事件的状况下是能够缩小很多反复工作的。

// 来实现把 #list 下的 li 元素的事件代理委托到它的父层元素也就是 #list 上:// 给父层元素绑定事件
document.getElementById('list').addEventListener('click', function (e) {
  // 兼容性解决
  var event = e || window.event;
  var target = event.target || event.srcElement;
  // 判断是否匹配指标元素
  if (target.nodeName.toLocaleLowerCase === 'li') {console.log('the content is:', target.innerHTML);
  }
});

在上述代码中,target 元素则是在 #list 元素之下具体被点击的元素,而后通过判断 target 的一些属性(比方:nodeName,id 等等)能够更准确地匹配到某一类 #list li 元素之上;

(3)局限性

当然,事件委托也是有局限的。比方 focus、blur 之类的事件没有事件冒泡机制,所以无奈实现事件委托;mousemove、mouseout 这样的事件,尽管有事件冒泡,然而只能一直通过地位去计算定位,对性能耗费高,因而也是不适宜于事件委托的。

当然事件委托不是只有长处,它也是有 毛病 的,事件委托会影响页面性能,次要影响因素有:

  • 元素中,绑定事件委托的次数;
  • 点击的最底层元素,到绑定事件元素之间的 DOM 层数;

在必须应用事件委托的中央,能够进行如下的解决:

  • 只在必须的中央,应用事件委托,比方:ajax的部分刷新区域
  • 尽量的缩小绑定的层级,不在 body 元素上,进行绑定
  • 缩小绑定的次数,如果能够,那么把多个事件的绑定,合并到一次事件委托中去,由这个事件委托的回调,来进行散发。

变量晋升

函数在运行的时候,会首先创立执行上下文,而后将执行上下文入栈,而后当此执行上下文处于栈顶时,开始运行执行上下文。

在创立执行上下文的过程中会做三件事:创立变量对象,创立作用域链,确定 this 指向,其中创立变量对象的过程中,首先会为 arguments 创立一个属性,值为 arguments,而后会扫码 function 函数申明,创立一个同名属性,值为函数的援用,接着会扫码 var 变量申明,创立一个同名属性,值为 undefined,这就是变量晋升。

在地址栏里输出一个地址回车会产生哪些事件

1、解析 URL:首先会对 URL 进行解析,剖析所须要应用的传输协定和申请的资源的门路。如果输出的 URL 中的协定或者主机名不非法,将会把地址栏中输出的内容传递给搜索引擎。如果没有问题,浏览器会查看 URL 中是否呈现了非法字符,如果存在非法字符,则对非法字符进行本义后再进行下一过程。2、缓存判断:浏览器会判断所申请的资源是否在缓存里,如果申请的资源在缓存里并且没有生效,那么就间接应用,否则向服务器发动新的申请。3、DNS 解析:下一步首先须要获取的是输出的 URL 中的域名的 IP 地址,首先会判断本地是否有该域名的 IP 地址的缓存,如果有则应用,如果没有则向本地 DNS 服务器发动申请。本地 DNS 服务器也会先查看是否存在缓存,如果没有就会先向根域名服务器发动申请,取得负责的顶级域名服务器的地址后,再向顶级域名服务器申请,而后取得负责的权威域名服务器的地址后,再向权威域名服务器发动申请,最终取得域名的 IP 地址后,本地 DNS 服务器再将这个 IP 地址返回给申请的用户。用户向本地 DNS 服务器发动申请属于递归申请,本地 DNS 服务器向各级域名服务器发动申请属于迭代申请。4、获取 MAC 地址:当浏览器失去 IP 地址后,数据传输还须要晓得目标主机 MAC 地址,因为应用层下发数据给传输层,TCP 协定会指定源端口号和目标端口号,而后下发给网络层。网络层会将本机地址作为源地址,获取的 IP 地址作为目标地址。而后将下发给数据链路层,数据链路层的发送须要退出通信单方的 MAC 地址,本机的 MAC 地址作为源 MAC 地址,目标 MAC 地址须要分状况解决。通过将 IP 地址与本机的子网掩码相与,能够判断是否与申请主机在同一个子网里,如果在同一个子网里,能够应用 APR 协定获取到目标主机的 MAC 地址,如果不在一个子网里,那么申请应该转发给网关,由它代为转发,此时同样能够通过 ARP 协定来获取网关的 MAC 地址,此时目标主机的 MAC 地址应该为网关的地址。5、TCP 三次握手:上面是 TCP 建设连贯的三次握手的过程,首先客户端向服务器发送一个 SYN 连贯申请报文段和一个随机序号,服务端接管到申请后向客户端发送一个 SYN ACK 报文段,确认连贯申请,并且也向客户端发送一个随机序号。客户端接管服务器的确认应答后,进入连贯建设的状态,同时向服务器也发送一个 ACK 确认报文段,服务器端接管到确认后,也进入连贯建设状态,此时单方的连贯就建设起来了。6、HTTPS 握手:如果应用的是 HTTPS 协定,在通信前还存在 TLS 的一个四次握手的过程。首先由客户端向服务器端发送应用的协定的版本号、一个随机数和能够应用的加密办法。服务器端收到后,确认加密的办法,也向客户端发送一个随机数和本人的数字证书。客户端收到后,首先查看数字证书是否无效,如果无效,则再生成一个随机数,并应用证书中的公钥对随机数加密,而后发送给服务器端,并且还会提供一个后面所有内容的 hash 值供服务器端测验。服务器端接管后,应用本人的私钥对数据解密,同时向客户端发送一个后面所有内容的 hash 值供客户端测验。这个时候单方都有了三个随机数,依照之前所约定的加密办法,应用这三个随机数生成一把秘钥,当前单方通信前,就应用这个秘钥对数据进行加密后再传输。7、返回数据:当页面申请发送到服务器端后,服务器端会返回一个 html 文件作为响应,浏览器接管到响应后,开始对 html 文件进行解析,开始页面的渲染过程。8、页面渲染:浏览器首先会依据 html 文件构建 DOM 树,依据解析到的 css 文件构建 CSSOM 树,如果遇到 script 标签,则判端是否含有 defer 或者 async 属性,要不然 script 的加载和执行会造成页面的渲染的阻塞。当 DOM 树和 CSSOM 树建设好后,依据它们来构建渲染树。渲染树构建好后,会依据渲染树来进行布局。布局实现后,最初应用浏览器的 UI 接口对页面进行绘制。这个时候整个页面就显示进去了。9、TCP 四次挥手:最初一步是 TCP 断开连接的四次挥手过程。若客户端认为数据发送实现,则它须要向服务端发送连贯开释申请。服务端收到连贯开释申请后,会通知应用层要开释 TCP 链接。而后会发送 ACK 包,并进入 CLOSE_WAIT 状态,此时表明客户端到服务端的连贯曾经开释,不再接管客户端发的数据了。然而因为 TCP 连贯是双向的,所以服务端仍旧能够发送数据给客户端。服务端如果此时还有没发完的数据会持续发送,结束后会向客户端发送连贯开释申请,而后服务端便进入 LAST-ACK 状态。客户端收到开释申请后,向服务端发送确认应答,此时客户端进入 TIME-WAIT 状态。该状态会继续 2MSL(最大段生存期,指报文段在网络中生存的工夫,超时会被摈弃)工夫,若该时间段内没有服务端的重发申请的话,就进入 CLOSED 状态。当服务端收到确认应答后,也便进入 CLOSED 状态。

PWA 应用过吗?serviceWorker 的应用原理是啥?

渐进式网络应用(PWA)是谷歌在 2015 年底提出的概念。基本上算是 web 应用程序,但在外观和感觉上与 原生 app相似。反对 PWA 的网站能够提供脱机工作、推送告诉和设施硬件拜访等性能。

Service Worker是浏览器在后盾独立于网页运行的脚本,它关上了通向不须要网页或用户交互的性能的大门。当初,它们已包含如推送告诉和后盾同步等性能。未来,Service Worker将会反对如定期同步或天文围栏等其余性能。本教程探讨的外围性能是拦挡和解决网络申请,包含通过程序来治理缓存中的响应。

谈谈你对状态治理的了解

  • 首先介绍 Flux,Flux 是一种应用单向数据流的模式来组合 React 组件的利用架构。
  • Flux 蕴含了 4 个局部,别离是 DispatcherStoreViewActionStore 存储了视图层所有的数据,当 Store 变动后会引起 View 层的更新。如果在视图层触发一个 Action,就会使以后的页面数据值发生变化。Action 会被 Dispatcher 进行对立的收发解决,传递给 Store 层,Store 层曾经注册过相干 Action 的解决逻辑,解决对应的外部状态变动后,触发 View 层更新。
  • Flux 的长处是单向数据流,解决了 MVC 中数据流向不清的问题,使开发者能够疾速理解利用行为。从我的项目构造上简化了视图层设计,明确了分工,数据与业务逻辑也对立寄存治理,使在大型架构的我的项目中更容易治理、保护代码。
  • 其次是 Redux,Redux 自身是一个 JavaScript 状态容器,提供可预测化状态的治理。社区通常认为 Redux 是 Flux 的一个简化设计版本,它提供的状态治理,简化了一些高级个性的实现老本,比方撤销、重做、实时编辑、工夫旅行、服务端同构等。
  • Redux 的外围设计蕴含了三大准则:繁多数据源、纯函数 Reducer、State 是只读的
  • Redux 中整个数据流的计划与 Flux 大同小异
  • Redux 中的另一大外围点是解决“副作用”,AJAX 申请等异步工作,或不是纯函数产生的第三方的交互都被认为是“副作用”。这就造成在纯函数设计的 Redux 中,解决副作用变成了一件至关重要的事件。社区通常有两种解决方案:

    • 第一类是在 Dispatch 的时候会有一个 middleware 中间件层,拦挡散发的 Action 并增加额定的简单行为,还能够增加副作用。第一类计划的风行框架有 Redux-thunk、Redux-Promise、Redux-Observable、Redux-Saga 等。
    • 第二类是容许 Reducer 层中间接解决副作用,采取该计划的有 React LoopReact Loop 在实现中采纳了 Elm 中分形的思维,使代码具备更强的组合能力。
    • 除此以外,社区还提供了更为工程化的计划,比方 rematch 或 dva,提供了更具体的模块架构能力,提供了拓展插件以反对更多功能。
  • Redux 的长处很多:

    • 后果可预测;
    • 代码构造严格易保护;
    • 模块拆散清晰且小函数构造容易编写单元测试;
    • Action 触发的形式,能够在调试器中应用工夫回溯,定位问题更简略快捷;
    • 繁多数据源使服务端同构变得更为容易;社区计划多,生态也更为凋敝。
  • 最初是 Mobx,Mobx 通过监听数据的属性变动,能够间接在数据上更改触发 UI 的渲染。在应用上更靠近 Vue,比起 Flux 与 Redux 的手动挡的体验,更像开自动挡的汽车。Mobx 的响应式实现原理与 Vue 雷同 ,以 Mobx 5 为分界点,5 以前采纳 Object.defineProperty 的计划,5 及当前应用 Proxy 的计划。 它的长处是样板代码少、简略粗犷、用户学习快、响应式自动更新数据 让开发者的心智累赘更低。
  • Mobx 在开发我的项目时简略疾速,但利用 Mobx 的场景,其实齐全能够用 Vue 取代。如果纯用 Vue,体积还会更玲珑

深浅拷贝

浅拷贝:只思考对象类型。

function shallowCopy(obj) {if (typeof obj !== 'object') return

    let newObj = obj instanceof Array ? [] : {}
    for (let key in obj) {if (obj.hasOwnProperty(key)) {newObj[key] = obj[key]
        }
    }
    return newObj
}

简略版深拷贝:只思考一般对象属性,不思考内置对象和函数。

function deepClone(obj) {if (typeof obj !== 'object') return;
    var newObj = obj instanceof Array ? [] : {};
    for (var key in obj) {if (obj.hasOwnProperty(key)) {newObj[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key];
        }
    }
    return newObj;
}

简单版深克隆:基于简略版的根底上,还思考了内置对象比方 Date、RegExp 等对象和函数以及解决了循环援用的问题。

const isObject = (target) => (typeof target === "object" || typeof target === "function") && target !== null;

function deepClone(target, map = new WeakMap()) {if (map.get(target)) {return target;}
    // 获取以后值的构造函数:获取它的类型
    let constructor = target.constructor;
    // 检测以后对象 target 是否与正则、日期格局对象匹配
    if (/^(RegExp|Date)$/i.test(constructor.name)) {// 创立一个新的非凡对象 (正则类 / 日期类) 的实例
        return new constructor(target);  
    }
    if (isObject(target)) {map.set(target, true);  // 为循环援用的对象做标记
        const cloneTarget = Array.isArray(target) ? [] : {};
        for (let prop in target) {if (target.hasOwnProperty(prop)) {cloneTarget[prop] = deepClone(target[prop], map);
            }
        }
        return cloneTarget;
    } else {return target;}
}

参考 前端进阶面试题具体解答

哪些操作会造成内存透露?

  • 第一种状况是因为应用未声明的变量,而意外的创立了一个全局变量,而使这个变量始终留在内存中无奈被回收。
  • 第二种状况是设置了 setInterval 定时器,而遗记勾销它,如果循环函数有对外部变量的援用的话,那么这个变量会被始终留在内存中,而无奈被回收。
  • 第三种状况是获取一个 DOM 元素的援用,而前面这个元素被删除,因为咱们始终保留了对这个元素的援用,所以它也无奈被回收。
  • 第四种状况是不合理的应用闭包,从而导致某些变量始终被留在内存当中。

代码输入问题

function A(){}
function B(a){this.a = a;}
function C(a){if(a){this.a = a;}
}
A.prototype.a = 1;
B.prototype.a = 1;
C.prototype.a = 1;

console.log(new A().a);
console.log(new B().a);
console.log(new C(2).a);

输入后果:1 undefined 2

解析:

  1. console.log(new A().a),new A()为构造函数创立的对象,自身没有 a 属性,所以向它的原型去找,发现原型的 a 属性的属性值为 1,故该输入值为 1;
  2. console.log(new B().a),ew B()为构造函数创立的对象,该构造函数有参数 a,但该对象没有传参,故该输入值为 undefined;
  3. console.log(new C(2).a),new C()为构造函数创立的对象,该构造函数有参数 a,且传的实参为 2,执行函数外部,发现 if 为真,执行 this.a = 2, 故属性 a 的值为 2。

代码输入后果

Promise.resolve(1)
  .then(2)
  .then(Promise.resolve(3))
  .then(console.log)

输入后果如下:

1
Promise {<fulfilled>: undefined}

Promise.resolve 办法的参数如果是一个原始值,或者是一个不具备 then 办法的对象,则 Promise.resolve 办法返回一个新的 Promise 对象,状态为 resolved,Promise.resolve 办法的参数,会同时传给回调函数。

then 办法承受的参数是函数,而如果传递的并非是一个函数,它实际上会将其解释为 then(null),这就会导致前一个 Promise 的后果会传递上面。

原型

JavaScript 中的对象都有一个非凡的 prototype 内置属性,其实就是对其余对象的援用
简直所有的对象在创立时 prototype 属性都会被赋予一个非空的值,咱们能够把这个属性当作一个备用的仓库
当试图援用对象的属性时会登程 get 操作,第一步时查看对象自身是否有这个属性,如果有就应用它,没有就去原型中查找。一层层向上直到 Object.prototype 顶层

基于原型扩大形容一下原型链,什么是原型链,原型的继承,ES5 和 ES6 继承与不同点。

Nginx 的概念及其工作原理

Nginx 是一款轻量级的 Web 服务器,也能够用于反向代理、负载平衡和 HTTP 缓存等。Nginx 应用异步事件驱动的办法来解决申请,是一款面向性能设计的 HTTP 服务器。

传统的 Web 服务器如 Apache 是 process-based 模型的,而 Nginx 是基于 event-driven 模型的。正是这个次要的区别带给了 Nginx 在性能上的劣势。

Nginx 架构的最顶层是一个 master process,这个 master process 用于产生其余的 worker process,这一点和 Apache 十分像,然而 Nginx 的 worker process 能够同时解决大量的 HTTP 申请,而每个 Apache process 只能解决一个。

继承

原型继承

核心思想:子类的原型成为父类的实例

实现

function SuperType() {this.colors = ['red', 'green'];
}
function SubType() {}
// 原型继承要害: 子类的原型成为父类的实例
SubType.prototype = new SuperType();

// 测试
let instance1 = new SubType();
instance1.colors.push('blue');

let instance2 = new SubType();
console.log(instance2.colors);  // ['red', 'green', 'blue']

原型继承存在的问题

  1. 原型中蕴含的援用类型属性将被所有实例对象共享
  2. 子类在实例化时不能给父类构造函数传参

构造函数继承

核心思想:在子类构造函数中调用父类构造函数

实现

function SuperType(name) {
    this.name = name;
    this.colors = ['red', 'green'];
    this.getName = function() {return this.name;}
}
function SubType(name) {
    // 继承 SuperType 并传参
    SuperType.call(this, name);
}

// 测试
let instance1 = new SubType('instance1');
instance1.colors.push('blue');
console.log(instance1.colors); // ['red','green','blue']

let instance2 = new SubType('instance2');
console.log(instance2.colors);  // ['red', 'green']

构造函数继承 的呈现是为了解决了原型继承的援用值共享问题。长处是能够在子类构造函数中向父类构造函数传参。它存在的问题是:1)因为办法必须在构造函数中定义,因而办法不能重用。2)子类也不能拜访父类原型上定义的办法。

组合继承

核心思想:综合了原型链和构造函数,即,应用原型链继承原型上的办法,而通过构造函数继承实例属性。

实现

function SuperType(name) {
    this.name = name;
    this.colors = ['red', 'green'];
}
Super.prototype.sayName = function() {console.log(this.name);
}
function SubType(name, age) {
    // 继承属性
    SuperType.call(this, name);
    // 实例属性
    this.age = age;
}
// 继承办法
SubType.prototype = new SuperType();

// 测试
let instance1 = new SubType('instance1', 1);
instance1.sayName();  // "instance1"
instance1.colors.push("blue");
console.log(instance1.colors); // ['red','green','blue']

let instance2 = new SubType('instance2', 2);
instance2.sayName();  // "instance2"
console.log(instance2.colors); // ['red','green']

组合继承 存在的问题是:父类构造函数始终会被调用两次:一次是在创立子类原型时 new SuperType() 调用,另一次是在子类构造函数中 SuperType.call() 调用。

寄生式组合继承(最佳)

核心思想:通过构造函数继承属性,但应用混合式原型继承办法,即,不通过调用父类构造函数给子类原型赋值,而是获得父类原型的一个正本。

实现

function SuperType(name) {
    this.name = name;
    this.colors = ['red', 'green'];
}
Super.prototype.sayName = function() {console.log(this.name);
}
function SubType(name, age) {
    // 继承属性
    SuperType.call(this, name);
    this.age = age;
}
// 继承办法
SubType.prototype = Object.create(SuperType.prototype);
// 重写原型导致默认 constructor 失落,手动将 constructor 指回 SubType
SubType.prototype.constructor = SubType;

class 实现继承(ES6)

核心思想:通过 extends 来实现类的继承(相当于 ES5 的原型继承)。通过 super 调用父类的构造方法 (相当于 ES5 的构造函数继承)。

实现

class SuperType {constructor(name) {this.name = name;}
    sayName() {console.log(this.name);
    }
}
class SubType extends SuperType {constructor(name, age) {super(name);  // 继承属性
        this.age = age;
    }
}

// 测试
let instance = new SubType('instance', 0);
instance.sayName();  // "instance"

尽管类继承应用的是新语法,但背地仍旧应用的是原型链。

什么是作用域?

ES5 中只存在两种作用域:全局作用域和函数作用域。在 JavaScript 中,咱们将作用域定义为一套规定,这套规定用来治理引擎如何在以后作用域以及嵌套子作用域中依据标识符名称进行变量(变量名或者函数名)查找

手写题:Promise 原理

class MyPromise {constructor(fn) {this.callbacks = [];
    this.state = "PENDING";
    this.value = null;

    fn(this._resolve.bind(this), this._reject.bind(this));
  }

  then(onFulfilled, onRejected) {return new MyPromise((resolve, reject) =>
      this._handle({
        onFulfilled: onFulfilled || null,
        onRejected: onRejected || null,
        resolve,
        reject,
      })
    );
  }

  catch(onRejected) {return this.then(null, onRejected);
  }

  _handle(callback) {if (this.state === "PENDING") {this.callbacks.push(callback);

      return;
    }

    let cb =
      this.state === "FULFILLED" ? callback.onFulfilled : callback.onRejected;
    if (!cb) {
      cb = this.state === "FULFILLED" ? callback.resolve : callback.reject;
      cb(this.value);

      return;
    }

    let ret;

    try {ret = cb(this.value);
      cb = this.state === "FULFILLED" ? callback.resolve : callback.reject;
    } catch (error) {
      ret = error;
      cb = callback.reject;
    } finally {cb(ret);
    }
  }

  _resolve(value) {if (value && (typeof value === "object" || typeof value === "function")) {
      let then = value.then;

      if (typeof then === "function") {then.call(value, this._resolve.bind(this), this._reject.bind(this));

        return;
      }
    }

    this.state === "FULFILLED";
    this.value = value;
    this.callbacks.forEach((fn) => this._handle(fn));
  }

  _reject(error) {
    this.state === "REJECTED";
    this.value = error;
    this.callbacks.forEach((fn) => this._handle(fn));
  }
}

const p1 = new Promise(function (resolve, reject) {setTimeout(() => reject(new Error("fail")), 3000);
});

const p2 = new Promise(function (resolve, reject) {setTimeout(() => resolve(p1), 1000);
});

p2.then((result) => console.log(result)).catch((error) => console.log(error));

Promise.race

形容 :只有promises 中有一个率先扭转状态,就返回这个率先扭转的 Promise 实例的返回值。

实现

Promise.race = function(promises){return new Promise((resolve, reject) => {if(Array.isArray(promises)) {if(promises.length === 0) return resolve(promises);
            promises.forEach((item) => {Promise.resolve(item).then(value => resolve(value), 
                    reason => reject(reason)
                );
            })
        }
        else return reject(new TypeError("Argument is not iterable"));
    });
}

instanceof

题目形容: 手写 instanceof 操作符实现

实现代码如下:

function myInstanceof(left, right) {while (true) {if (left === null) {return false;}
    if (left.__proto__ === right.prototype) {return true;}
    left = left.__proto__;
  }
}

对 Flex 布局的了解及其应用场景

Flex 是 FlexibleBox 的缩写,意为 ” 弹性布局 ”,用来为盒状模型提供最大的灵活性。任何一个容器都能够指定为 Flex 布局。行内元素也能够应用 Flex 布局。留神,设为 Flex 布局当前,子元素的 float、clear 和 vertical-align 属性将生效。采纳 Flex 布局的元素,称为 Flex 容器(flex container),简称 ” 容器 ”。它的所有子元素主动成为容器成员,称为 Flex 我的项目(flex item),简称 ” 我的项目 ”。容器默认存在两根轴:程度的主轴(main axis)和垂直的穿插轴(cross axis),我的项目默认沿程度主轴排列。

以下 6 个属性设置在 容器上

  • flex-direction 属性决定主轴的方向(即我的项目的排列方向)。
  • flex-wrap 属性定义,如果一条轴线排不下,如何换行。
  • flex-flow 属性是 flex-direction 属性和 flex-wrap 属性的简写模式,默认值为 row nowrap。
  • justify-content 属性定义了我的项目在主轴上的对齐形式。
  • align-items 属性定义我的项目在穿插轴上如何对齐。
  • align-content 属性定义了多根轴线的对齐形式。如果我的项目只有一根轴线,该属性不起作用。

以下 6 个属性设置在 我的项目上

  • order 属性定义我的项目的排列程序。数值越小,排列越靠前,默认为 0。
  • flex-grow 属性定义我的项目的放大比例,默认为 0,即如果存在残余空间,也不放大。
  • flex-shrink 属性定义了我的项目的放大比例,默认为 1,即如果空间有余,该我的项目将放大。
  • flex-basis 属性定义了在调配多余空间之前,我的项目占据的主轴空间。浏览器依据这个属性,计算主轴是否有多余空间。它的默认值为 auto,即我的项目的原本大小。
  • flex 属性是 flex-grow,flex-shrink 和 flex-basis 的简写,默认值为 0 1 auto。
  • align-self 属性容许单个我的项目有与其余我的项目不一样的对齐形式,可笼罩 align-items 属性。默认值为 auto,示意继承父元素的 align-items 属性,如果没有父元素,则等同于 stretch。

简略来说: flex 布局是 CSS3 新增的一种布局形式,能够通过将一个元素的 display 属性值设置为 flex 从而使它成为一个 flex 容器,它的所有子元素都会成为它的我的项目。一个容器默认有两条轴:一个是程度的主轴,一个是与主轴垂直的穿插轴。能够应用 flex-direction 来指定主轴的方向。能够应用 justify-content 来指定元素在主轴上的排列形式,应用 align-items 来指定元素在穿插轴上的排列形式。还能够应用 flex-wrap 来规定当一行排列不下时的换行形式。对于容器中的我的项目,能够应用 order 属性来指定我的项目的排列程序,还能够应用 flex-grow 来指定当排列空间有残余的时候,我的项目的放大比例,还能够应用 flex-shrink 来指定当排列空间有余时,我的项目的放大比例。

LRU 算法

实现代码如下:

//  一个 Map 对象在迭代时会依据对象中元素的插入程序来进行
// 新增加的元素会被插入到 map 的开端,整个栈倒序查看
class LRUCache {constructor(capacity) {this.secretKey = new Map();
    this.capacity = capacity;
  }
  get(key) {if (this.secretKey.has(key)) {let tempValue = this.secretKey.get(key);
      this.secretKey.delete(key);
      this.secretKey.set(key, tempValue);
      return tempValue;
    } else return -1;
  }
  put(key, value) {
    // key 存在,仅批改值
    if (this.secretKey.has(key)) {this.secretKey.delete(key);
      this.secretKey.set(key, value);
    }
    // key 不存在,cache 未满
    else if (this.secretKey.size < this.capacity) {this.secretKey.set(key, value);
    }
    // 增加新 key,删除旧 key
    else {this.secretKey.set(key, value);
      // 删除 map 的第一个元素,即为最长未应用的
      this.secretKey.delete(this.secretKey.keys().next().value);
    }
  }
}
// let cache = new LRUCache(2);
// cache.put(1, 1);
// cache.put(2, 2);
// console.log("cache.get(1)", cache.get(1))// 返回  1
// cache.put(3, 3);// 该操作会使得密钥 2 作废
// console.log("cache.get(2)", cache.get(2))// 返回 -1 (未找到)
// cache.put(4, 4);// 该操作会使得密钥 1 作废
// console.log("cache.get(1)", cache.get(1))// 返回 -1 (未找到)
// console.log("cache.get(3)", cache.get(3))// 返回  3
// console.log("cache.get(4)", cache.get(4))// 返回  4

其余值到布尔类型的值的转换规则?

以下这些是假值:
• undefined
• null
• false
• +0、-0 和 NaN
• “”

假值的布尔强制类型转换后果为 false。从逻辑上说,假值列表以外的都应该是真值。

Promise.all 和 Promise.race 的区别的应用场景

(1)Promise.all Promise.all能够将多个 Promise 实例包装成一个新的 Promise 实例。同时,胜利和失败的返回值是不同的,胜利的时候返回的是 一个后果数组 ,而失败的时候则返回 最先被 reject 失败状态的值

Promise.all 中传入的是数组,返回的也是是数组,并且会将进行映射,传入的 promise 对象返回的值是依照程序在数组中排列的,然而留神的是他们执行的程序并不是依照程序的,除非可迭代对象为空。

须要留神,Promise.all 取得的胜利后果的数组外面的数据程序和 Promise.all 接管到的数组程序是统一的,这样当遇到发送多个申请并依据申请程序获取和应用数据的场景,就能够应用 Promise.all 来解决。

(2)Promise.race

顾名思义,Promse.race 就是赛跑的意思,意思就是说,Promise.race([p1, p2, p3])外面哪个后果取得的快,就返回那个后果,不论后果自身是胜利状态还是失败状态。当要做一件事,超过多长时间就不做了,能够用这个办法来解决:

Promise.race([promise1,timeOutPromise(5000)]).then(res=>{})
正文完
 0