Skip to content

Latest commit

 

History

History
920 lines (715 loc) · 40.7 KB

面试中遇到的不会的问题.md

File metadata and controls

920 lines (715 loc) · 40.7 KB

数组降维度 flat(n),当n为1时和展开运算符一样(...)降低1维度 n可以指定的n层深度递归遍历数组。

flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。

const arr1 = [0, 1, 2, [3, 4]];

console.log(arr1.flat()); // expected output: [0, 1, 2, 3, 4]

const arr2 = [0, 1, 2, [[[3, 4]]]];

console.log(arr2.flat(2)); // expected output: [0, 1, 2, [3, 4]]

//查找字符串中是否含有某个字符 //方法一 //indexOf()可返回指定的字符串值在字符串中首次出现的位置,如果没有,返回-1 var str = "abc"; console.log(str.indexOf("abc") != -1);

//方法二 search() 用于检索字符串中指定的字符串,或检索与正则表达式相匹配的子字符串 //如果没有返回-1 var str = "1234"; console.log(str.search("3") != -1);

//方法三 match()可在字符串内检索指定的值,或找到一个或多个正则表达式的匹配 var str = "123"; var reg = RegExp(/3/); if(str.match(reg)) { console.log("成功!"); } console.log(str.match(reg)) //成功返回[ '3', index: 2, input: '123', groups: undefined ], 失败返回null

// replace 第一个参数为替换目标,第二个参数为要被替换对象 var str ="Visit Microsoft!"; console.log(str.replace(/Microsoft/,"W3School"));

//lastIndexOf() 返回最后一次字符出现的位置,没有的话返回-1 var str = "The full name of Chian is the Prople's Republic of China."; console.log(str.lastIndexOf("China"));

// slice()提取字符串的某部分,两个参数 开始和结束 //substring() 类似于 slice()。不同之处在于 substring() 无法接受负的索引。 var str = "Apple, Banana,Mango"; console.log(str.slice(7,13));

// substr()类似于slice,不同之处是第二个参数表示要提取部分的长度 var str = "Apple, Banana,Mango"; console.log(str.substr(7,6))

// 大小写转化 toUpperCase(),toLowerCase() var str = "Apple, Banana,Mango"; console.log(str.toUpperCase()); console.log(str.toLowerCase());

//连接两个字符串 var text1 = "Hello"; var text2 = "en"; var text3 = text1.concat(" ",text2); console.log(text3);

//删除字符串两端空白符 var str = " Hello World! "; console.log(str.trim());

//返回字符串中指定位置的字符串 var str = "HELLOWORLD"; console.log(str.charAt(0));

//charCodeAt()返回字符串中指定索引的字符的unicode编码 var str = "HELLOWORLD"; console.log(str.charCodeAt(0));

//将字符串转换为数组 var str = "a,b,c,d,f"; console.log(str.split(",")); //[ 'a', 'b', 'c', 'd', 'f' ] console.log(str.split(" ")); // [ 'a,b,c,d,f' ] console.log(str.split("|")); // [ 'a,b,c,d,f' ]

// let相比var 没有变量提升,有暂时性死区:没有声明不能使用。let只在代码块内有效

// 匿名函数模仿块级作用域 (function() { //这里是块级作用域 })();

// 关于匿名函数的讲解 var count = 5; outputNumbers(count); // 等同于 outputNumbers(5); // 演化一下

var someFunction = funciton() { // 这里是块级作用域 }; //调用执行方法 someFunction(); //等同于 (function() { //块级作用域 })();

20200601 // symbol 作为一个对象或一个Map的键值,他可以保证你的对象或Map键值不重复。 var privateKey = Symbol(); var obj = { [privateKey] : 'Hero' } // 访问时 console.log(obj[privateKey]); //Hero

// 数据结构Set,类似于数组,成员的值都是唯一的,没有重复值 const s = new Set(); [2,3,5,4,2,2].forEach(x => s.add(x)); for (let i of s) { console.log(i); } // 去除数组的重复成员 [...new Set(Array)]

const set = new Set([1,2,3,4,4]); [...set]

// set和map的区别就是集合和字典的区别 // JavaScript 的对象(Object),本质上是键值对的集合(Hash 结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制 // Map 数据结构,各种类型的值(包括对象)都可以当键。 // Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。 const m = new Map(); const o = {p: 'Hello World'}; m.set(o, 'content'); m.get(o); // 'content' m.has(o) //true m.delete(o) m.has(o) // false

// 再实现一次 const map = new Map([ ['name', '张三'], ['title', 'Author'] ]); map.size // 2 map.has('name') //true map.get('name') // 张三 map.has('title') //true map.get('title') //Author

// 居中 // div使用绝对布局 1.设置margin:auto;并设置top,left,right,bottom的值都相等。(由于宽高固定,因此对应方向实现平分,可以实现水 平和垂直方向上的居中。) 2.设置left和top都是50%,然后再用transform: translate(-50%, -50%); 向左平移它的宽度的50% (先将元素的左上角通过top:50%和left:50%定位到页面的中心,然后再通过translate来调整元素 的中心点到页面的中心。)

// div使用flex布局 设置justify-content:center; // 水平居中 align-items:center; //垂直居中

// 伪类和伪元素 在css3中使用单冒号来表示伪类,用双冒号来表示伪元素。但是为了兼容已有的伪元素的写法,在一些浏览器中也可以使用单冒号 来表示伪元素。

伪类一般匹配的是元素的一些特殊状态,如hover、link等,而伪元素一般匹配的特殊的位置,比如after、before等

// 使用hover当鼠标悬停到文字上时改变颜色 // 这种一般是借助hover事件,就是说当鼠标放到文字上时会触发一个事件,此时可以修改文字的样式。 // 另外一种简单的办法就是直接用css的hover属性设置样式。 /////直接用css的hover属性设置样式。保存.css, 在html代码引用! < a href="" class="test">链接内容 .test { color:#eeffee; } .test:hover { color:#ff0000; }

// 选择器 (1)id选择器(#myid) (2)类选择器(.myclassname) (3)标签选择器(div,h1,p) (4)后代选择器(h1p) (5)相邻后代选择器(子)选择器(ul>li) (6)兄弟选择器(li~a) (7)相邻兄弟选择器(li+a) (8)属性选择器(a[rel="external"]) (9)伪类选择器(a:hover,li:nth-child) (10)伪元素选择器(::before、::after) (11)通配符选择器(*)

// div嵌套div ,点击子级div触发父级div点击事件 解决方法: 在子级div上增加一个事件 onClick="event.cancelBubble = true",这样就可以只触发自身的点击事件了 或者在孩子事件里面声明event.stopPropagation();//终止事件冒泡

在父div的position为abosulte或是relative的情况下,子级div的position为absolute. 此时监听到了父div的缩放,并触发了事件。 那么js如何做到去操作子级的left和top,及width和height,随着父div的缩放而自适应呢? .a{ width: 500px; height: 500px; position:relative; border: 1px solid red; } .b{ width: 10%; height: 10%; position: absolute; left: 10%; top: 10%; border: 1px solid green; }

// 冒泡学习

Click me!

事件冒泡微软提出了名为事件冒泡(event bubbling)的事件流。事件冒泡可以形象地比喻为把一颗石头投入水中, 泡泡会一直从水底冒出水面。也就是说,事件会从最内层的元素开始发生,一直向上传播,直到document对象。 因此上面的例子在事件冒泡的概念下发生click事件的顺序应该是p -> div -> body -> html -> document

// 事件捕获 网景提出另一种事件流名为事件捕获(event capturing)。与事件冒泡相反,事件会从最外层开始发生,直到最具体的元素。

addEventListener方法用来为一个特定的元素绑定一个事件处理函数,是JavaScript中的常用方法。addEventListener有三个参数: element.addEventListener(event, function, useCapture) // useCapture 设置为 true - 事件句柄在捕获阶段执行(即在事件捕获阶段调用处理函数) // false- false- 默认。事件句柄在冒泡阶段执行(即表示在事件冒泡的阶段调用事件处理函数)

// cookie和session cookie通过在客户端记录信息确定用户身份(不可跨域),session通过在服务端记录信息确定用户身份 http是一种无状态协议,一旦数据交换完毕,客户端和服务端就会断开连接,再次交互数据需要重新建立连接。 cookie可以解决上面问题,服务器如果需要记录该用户状态,就使用response向客户端颁发一个cookie, 当客户端会把cookie保存起来,需要请求时,将请求网址和cookie一同提交给服务器,服务器进行辨认。

session相当于在服务器上面建立一份客户档案,客户来访的时候只需要查询客户档案表即可。 session机制决定了客户只能获取到自己的session,彼此独立,互不可见。 提高速度》内存里,客户第一次访问创建,生成后只要用户访问就更新维护。超过超时时间就失效。 生命周期:session浏览器启动关闭,session消失,而cookie预先设置生存周期,或永久保存到本地文件。

// 箭头函数 箭头函数会默认帮我们绑定外层this的值,所以在箭头函数中this的值和外层的this是一样的。 class Json { constructor() { console.log(this); // Json {} this.a = 12; this.fun = () => { console.log(this); // Json { a: 12, fun: [Function (anonymous)] } } } } let json = new Json(); // json.fun(); let eData = new Date(); eData.fun = json.fun; eData.fun(); // Json { a: 12, fun: [Function (anonymous)] }

// 作用域链 本质是一个指向变量对象的指针列表,它只引用但不实际包含变量对象。 // 用途:保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端始终都是当前执行的代码所在的环境的变量对象。 // 链的由来:如果这个环境是函数,则将其活动对象(activation object)作为变量对象。活动对象在最开始时只包含一个变量, // 即 arguments 对象(这个对象在全局环境中是不存在的)。作用域链中的下一个变量对象来自包含(外部)环境,而再下一个变量 //对象则来自下一个包含环境。这样,一直延续到全局执行环境;全局执行环境的变量对象始终都是作用域链中的最后一个对象。 function compare(value1, value2) { if (value1 < value2) { return -1; } else if (value1 > value2) { return 1; } else { return 0; } } var result = compare(5 ,10); // 在创建compare()函数时,会创建一个预先包含全局变量对象的作用域链,这个作用域链被保存到内部的[[Scope]]属性中 // 当调用compare()函数时,会为函数创建执行环境,然后通过赋值 函数 的 [[Scope]]属性中的对象构建起执行环境的作用域链。 // 此后又有活动对象(再次作为变量对象使用)被创建并被推入执行环境作用域链前端。对于compare函数执行环境而言 // 其作用域链中包含两个变量对象:本地活动对象和全局对象

// 总结一下: 当某个函数被调用时,会创建一个执行环境(execution context)及相应的作用域链。 // 然后,使用 arguments 和其他命名参数的值来初始化函数的活动对象(activation object)。但在作用域 // 链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象处于第三位,

文字超出一行,省略超出部分,显示'...' 用text-overflow:ellipsis属性来,当然还需要加宽度width属来兼容部分浏览。 overflow: hidden; text-overflow:ellipsis; white-space: nowrap; // 和 normal 一样,连续的空白符会被合并。但文本内的换行无效。

多行文本溢出显示省略号

display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden; 注:

        ①-webkit-line-clamp用来限制在一个块元素显示的文本的行数。 为了实现该效果,它需要组合其他的WebKit属性。常见结合属性:

        ②display: -webkit-box; 必须结合的属性 ,将对象作为弹性伸缩盒子模型显示 。

        ③-webkit-box-orient 必须结合的属性 ,设置或检索伸缩盒对象的子元素的排列方式 。

css过渡 transition-property: width; transition-duration: 1s; // linear线性速度,ease 慢-》快-》慢, // ease-in以慢速开始, ease-out以慢速结束 , ease-in-out 以慢速开始和结束 transition-timing-function: linear; transition-delay: 2s; //开始时间 } 缩写 div{ transition: width 1s linear 2s; }

css动画 请用百分比来规定变化发生的时间,或用关键词 "from" 和 "to",等同于 0% 和 100%。 0% 是动画的开始,100% 是动画的完成。

当您在 @keyframes 中创建动画时,请把它捆绑到某个选择器,否则不会产生动画效果。 通过规定至少以下两项 CSS3 动画属性,即可将动画绑定到选择器: 规定动画的名称 规定动画的时长

定义动画: 例子1: @keyframes myfirst { 0% {background: red;} 25% {background: yellow;} 50% {background: blue;} 100% {background: green;} } 例子2: @keyframes myfirst { from {background: red;} to {background: yellow;} } 运用: 例子1:把 "myfirst" 动画捆绑到 div 元素,时长:5 秒: div { animation: myfirst 5s; } 例子2: div { animation-name: myfirst; animation-duration: 5s; animation-timing-function: linear; // 运动节奏 animation-delay: 2s; // 何时开始,这个就是等待两秒然后开始 animation-iteration-count: infinite; animation-direction: alternate; // 规定动画在下一周期逆向地播放 animation-play-state: running; } 例子3: div { animation: myfirst 5s linear 2s infinite alternate; } 注意: animation 所有动画属性的简写属性,除了 animation-play-state 属性。 animation-fill-mode 设置CSS动画在执行之前和之后如何将样式应用于其目标: forwards 最后会保留变化后的状态 backwards 立即应用第一个关键帧中定义的值,最后回到原先状态 both 立即应用,最后还保留状态

set、map实际中的应用 set: Set和Array的区别是Set里的每一个元素都是唯一的,比如你有一个Array 里面有4个元素[1, 2, 3, 1],如果你把它转换成Set,就变成[1, 2, 3]了。 所以我们可以利用Set的这个特性做整数数组的去重。但要注意的是,如果你的 数组是[{name: 1}, {name: 1}]这样的包含Object的数组,那么你把它转换 为Set时并不能去重,因为虽然这两个对象看起来一样,但在Set看来,这个数组里 的两个对象是两个完全不同的值,所以并不符合去重的要求。

map: Map和Object很类似,但Map有一个比较特殊的应用场景。如果你在开发时不确定 键值对里面的键的名称,那么你需要用Map。举例来说,你需要动态地从MongoDB里 获取键值并显示给用户,但也许你并不关心这些键值的键名(利用了map值-值的特性即键名可以是任意的)到底是什么,那么在这种 情况下,你可以用Map,例如这样: var myMap = new Map();

var keyObj = {}, keyFunc = function () { return 'hey'}, keyString = "a string";

myMap.set(keyString, "value associated with 'a string'"); myMap.set(keyObj, "value associated with keyObj"); myMap.set(keyFunc, "value associated with keyFunc"); console.log(myMap.get(keyFunc));

打印出的是 value associated with keyFunc

const对象 声明必须初始化,全局常量不会变为window的对象属性。 const声明创建一个值的只读引用。但这并不意味着它所持有的值是不可变的, 只是变量标识符不能重新分配。例如,在引用内容是对象的情况下,这意味着 可以改变对象的内容(例如,其参数)。 // 常量可以定义成对象 const MY_OBJECT = {"key": "value"}; // 对象属性并不在保护的范围内,下面这个声明会成功执行 MY_OBJECT.key = "otherValue"; // 也可以用来定义数组 const MY_ARRAY = []; // It's possible to push items into the array // 可以向数组填充数据 MY_ARRAY.push('A'); // ["A"] // 但是,将一个新数组赋给变量会引发错误 MY_ARRAY = ['B']

call和apply的性能对比

结论:call比apply快 在看源码的过程中,总会遇到这样的写法:var triggerEvents = function(events, args) { var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2]; switch (args.length) { case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return; case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return; case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return; case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return; default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return; } }; 复制代码( 代码来自 backbone ) 作者会在参数为3个(包含3)以内时,优先使用 call 方法进行事件的处理。 而当参数过多(多余3个)时,才考虑使用 apply 方法。这个的原因就是 call 比 apply 快。

具体原因: call 方法比 apply 快的原因是 call 方法的参数格式正是内部方法所需要的格式。

Function.prototype.apply (thisArg, argArray)

1、如果 IsCallable(Function)为false,即 Function 不可以被调用,则抛出一个 TypeError 异常。 2、如果 argArray 为 null 或未定义,则返回调用 Function 的 [[Call]] 内部方法的结果,提供thisArg 和一个空数组作为参数。 3、如果 Type(argArray)不是 Object,则抛出 TypeError 异常。 4、获取 argArray 的长度。调用 argArray 的 [[Get]] 内部方法,找到属性 length。 赋值给 len。 5、定义 n 为 ToUint32(len)。 6、初始化 argList 为一个空列表。 7、初始化 index 为 0。 8、循环迭代取出 argArray。重复循环 while(index < n) a、将下标转换成String类型。初始化 indexName 为 ToString(index). b、定义 nextArg 为 使用 indexName 作为参数调用argArray的[[Get]]内部方法的结果。 c、将 nextArg 添加到 argList 中,作为最后一个元素。 d、设置 index = index+19、返回调用 Function 的 [[Call]] 内部方法的结果, 提供 thisArg 作为该值,argList 作为参数列表。

Function.prototype.call (thisArg [ , arg1 [ , arg2, … ] ] )

1、如果 IsCallable(Function)为 false,即 Function 不可以被调用, 则抛出一个 TypeError 异常。 2、定义 argList 为一个空列表。 3、如果使用超过一个参数调用此方法,则以从arg1开始的从左到右的顺序将每个参数附加为 argList 的最后一个元素 4、返回调用func的[[Call]]内部方法的结果,提供 thisArg 作为该值,argList 作为参数列表。 我们可以看到,明显 apply 比 call 的步骤多很多。由于 apply 中定义的参数格式(数组), 使得被调用之后需要做更多的事,需要将给定的参数格式改变(步骤8)。 同时也有一些对参数的检查(步骤2),在 call 中却是不必要的。 另外一个很重要的点:在 apply 中不管有多少个参数,都会执行循环,也就是步骤 6-8, 在 call 中也就是对应步骤3 ,是有需要才会被执行。

ES5和ES6的继承区别 好文章解析:

https://www.cnblogs.com/annika/p/9073572.html 一、实质

ES5的继承实质:先创建子类,再实例化父类,并将父类的方法添加添加到子类this中。通过原型或构造函数机制来实现。

ES6继承的实质: 先创建父类,实例化子类中通过调用super方法访问父级后,在通过修改this实现继承。如果不调用super方法,子类得不到this对象

es6如何实现继承:ES6实现继承是通过关键字extends、super来实现继承。

(通过class关键字定义类,里面有构造方法,类之间通过extends关键字实现继承。子类必须在constructor方法中调用super方法,否则新建实例报错。 因为子类没有自己的this对象,而是继承了父类的this对象,然后对其调用。如果不调用super方法,子类得不到this对象)

注意:super关键字指代父类的实例,即父类的this对象。在子类构造函数中,调用super后,才可使用this关键。

子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象, 然后对其进行加工,如果不调用super方法,子类就得不到this对象。因此,只有调用super之后,才可以使用this关键字。

继承:

ES5 通过修改原型链进行继承,ES6 通过关键字extend进行继承,但是子类的constructor调用时,需要先利用super调用父类的构造方法。否则新建实例对象时,会报错。

ES5继承的实质,是通过将父类的方法添加到子类的实例中。 // es5 function Super(name) { this.name = name || 'nimo'; return this; } Super.prototype.getName = function () { return this.name; };

function Sub(ages) { this.ages = ages || 20; return this; } Sub.prototype = new Super(); Sub.prototype.constructor = Sub;

ES6继承的实质,是通过将父类的属性和方法添加到this中,即将父类的属性和方法添加到子类实例对象上, 即子类的实例其实是基于父类创建的。这也就是为什么在ES6继承中,子类的构造函数必须先 调用super方法才能调用this。 // es6 class Super { constructor(name) { this.name = name || 'nimo'; } getName() { return this.name; } }

class Sub extends Super { constructor(ages) { super(); this.ages = ages || 20; } }

ES6 与 ES5 中的继承有 2 个区别:第一个是,ES6 中子类会继承父类的属性, 第二个区别是,super() 与 A.call(this) 是不同的,在继承原生构造函数的情况下, 体现得很明显,ES6 中的子类实例可以继承原生构造函数实例的内部属性,而在 ES5 中做不到。

任务队列(宏任务队列可以有多个,微任务队列只有一个)

栈(主线程执行完成以后从任务队列里面一个一个取一个一个执行) ——》遇到异步函数如setTimeout交给指定模块处理(runtime运行环境提供的),继续同步代码执行 ——》异步函数达到触发条件,如定时器到时、Ajax请求返回,压入指定任务队列 ——》栈(主线程执行完成以后从任务队列里面一个一个取一个一个执行),如此循环。

众所周知,JavaScript的一大特点就是是单线程,所有任务都需要在主线程里排队等待执行。

而JavaScript里的任务又分为同步任务和异步任务两种,基于事件循环(Event Loop)机制执行任务。

同步任务作为首要任务会在主线程里执行,异步任务则被“发配”到由另一个线程管理的任务队列中等待处理。 异步任务符合条件(比如ajax请求到数据,setTimeout延时到期)后,会在任务队列中添加可执行“事件”, 等待主线程中的同步任务执行完毕到任务队列里读取当前可执行的任务,将其加入主线程中执行,以此循环。

根据HTML Standard中的描述,一个事件循环中的执行流程大致如下。

1.选择最早的任务

2.设置事件循环中当前任务为上一步中选择的任务

3.执行该任务

4.将事件循环中的当前任务重新设置为空

5.将主线程中执行的任务移除

6.执行Microtask中的任务

7.执行页面渲染步骤,更新UI

通过阅读Promise/A+规范,可以得知异步的实现可分为两个机制,分别是macro-task和micro-task。

Macrotasks包括: script(整体代码)、setTimeout, setInterval, setImmediate, I/O, UI Rendering;

Microtasks包括: process.nextTick, Promise, Object.observe, MutationObserver。

Macrotasks、Microtasks执行机制:

1.主线程执行完后会先到micro-task队列中读取可执行任务

2.主线程执行micro-task任务

3.主线程到macro-task任务队列中读取可执行任务

4.主线程执行macro-task任务

5....转到Step 1

这里注意的是,UI Rendering是在micro-task之后执行,需要在UI渲染之前执行的逻辑, 一般采用micro-task异步回调方式进行调用。

同样,micro-task队列不宜过长,给micro-task队列添加过多回调阻塞macro-task队列的 任务执行是小事,重点是这有可能会阻塞UI Render,导致页面不能更新。浏览器也会基于 性能方面的考虑,对micro-task中的任务个数进行限制。

BFC 块格式化上下文(Block Formatting Context,BFC)

BFC指的是块级格式化上下文,一个元素形成了BFC之后,那么它内部元素产生的布局不会影响到外部元素,外部元素的布局也 不会影响到BFC中的内部元素。一个BFC就像是一个隔离区域,和其他区域互不影响。

一般来说根元素是一个BFC区域,浮动和绝对定位的元素也会形成BFC,display属性的值为inline-block、flex这些 属性时也会创建BFC。还有就是元素的overflow的值不为visible时都会创建BFC。

float CSS属性指定一个元素应沿其容器的左侧或右侧放置, 允许文本和内联元素环绕它。该元素从网页的正常流动(文档流)中移除,尽管仍然保持部分的流动性。 clear CSS 属性指定一个元素是否必须移动(清除浮动后)到在它之前的浮动元素下面。 clear 属性适用于浮动和非浮动元素。

块格式化上下文对浮动定位与清除浮动都很重要。 浮动定位和清除浮动时只会应用于同一个BFC内的元素。浮动不会影响其它BFC中元素的布局, 而清除浮动只能清除同一BFC中在它前面的元素的浮动。外边距折叠(Margin collapsing) 也只会发生在属于同一BFC的块级元素之间。

下列方式会创建块格式化上下文:

根元素() 浮动元素(元素的 float 不是 none) 绝对定位元素(元素的 position 为 absolute 或 fixed) 行内块元素(元素的 display 为 inline-block) 表格单元格(元素的 display 为 table-cell,HTML表格单元格默认为该值) overflow 值不为 visible 的块元素 display 值为 flow-root 的元素 contain 值为 layout、content 或 paint 的元素 弹性元素(display 为 flex 或 inline-flex 元素的直接子元素)

透明度、visiblity、display区别 opacity 用来设置透明度 display 定义建立布局时元素生成的显示框类型 visibility 用来设置元素是否可见。 opacity、visibility、display 这三个属性分别取值 0、hidden、none 都能使元素在页面上看不见,但是他们在方方面面都还是有区别的。

1.是否占据空间: 使用 opacity 和 visibility 属性时,元素还是会占据页面空间的, 而使用 display 属性时,元素不占据页面空间。

2.对子元素的影响 opacity和display属性,父元素对子元素有影响,而visiblity子元素不受父元素影响

3.自身绑定的事件是否继续触发 使用 visibility 和 display 属性,自身的事件不会触发, 而使用 opacity 属性,自身绑定的事件还是会触发的。

4.是否影响其他元素触发事件 visibility 和 display 属性是不会影响其他元素触发事件的, 而 opacity 属性 如果遮挡住其他元素,其他的元素就不会触发事件了。

http状态码 资源请求方面的区别,在 http1.0 中,存在一些浪费带宽的现象, 例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了, 并且不支持断点续传功能,http1.1 则在请求头引入了 range 头域,它允许只请求资源的某个部分, 即返回码是 206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。

js关于自动插入分号的规则 规则1:有换行符,且下一个符号是不符合语法的,那么就尝试插入分号。 规则2:有换行符,且语法中规定此处不能有换行符,那么自动插入分号。 规则3:源代码结束处,不能形成完整的脚本或模块结构,那么就自动插入分号。

js关于no Line Terminator here规则(不能插入换行符的规则,负责语法失效) 1.带标签的continue语句,不能再continue后插入换行 2.带标签的break语句,不能再break后插入换行 3.return后不能插入换行 4.后自增、后自减运算符前不能插入换行 5.throw和Exception之间不能插入换行 6.async关键字后面不能插入换行 7.箭头函数的箭头前不能插入换行 8.yield之后不能插入换行

winter的重学前端: 1.实际上JavaScript对象的运行时是一个“属性的集合”,属性以字符串或者Symbol为key,以数据属性特征值或者访问器属性特征值为value。

Object.create = function(prototype){ var cls = function(){} cls.prototype = prototype; return new cls; } 这段代码创建了一个空函数作为类,并把传入的原型挂在了它的 prototype,最后创建了一个它的实例,根据 new 的行为,这将产生一个以传入的第一个 参数为原型的对象。这个函数无法做到与原生的 Object.create 一致,一个是不支持第二个参数,另一个是不支持 null 作为原型,所以放到今天意义已 经不大了。

留给你一个小练习:我们现在要实现一个红绿灯,把一个圆形 div 按照绿色 3 秒,黄色 1 秒,红色 2 秒循环改变背景色 function sleep(duration) { return new Promise(function(resolve) { setTimeout(resolve, duration); }) } async function changeColor(duration, color) { document.getElementById("traffic-light").style.background = color; await sleep(duration); } async function main() { while(true) { await changeColor(3000, 'green'); await changeColor(1000, 'yellow'); await changeColor(2000, 'red'); } } main();

// 实现一个add方法,使计算结果能够满足如下预期: add(1)(2)(3) = 6; add(1, 2, 3)(4) = 10; add(1)(2)(3)(4)(5) = 15;

function add() { // 第一次执行时,定义一个数组专门用来存储所有的参数 var _args = Array.prototype.slice.call(arguments);

// 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值
var _adder = function() {
    _args.push(...arguments);
    return _adder;
};

// 利用toString隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
_adder.toString = function () {
    return _args.reduce(function (a, b) {
        return a + b;
    });
}
return _adder;

}

add(1)(2)(3) // 6 add(1, 2, 3)(4) // 10 add(1)(2)(3)(4)(5) // 15 add(2, 6)(1) // 9

2.常见的移动端适配方法 适配方案有很多种,常见的方法有以下几种:

固定高度,宽度百分比:这种方法只适合简单、要求不高的webApp,几乎达不到大型项目的要求,属于过时的方法。 Media Query(媒体查询):现在比较主流的适配方案,比如常用的样式框架Bootstrap就是用这种方法,它能完成大部分项目需求,但是编写过于复杂。 flex布局:主流的布局方式,不仅适用于移动Web,网页上也表现良好,这也是现在工作中用的最多的布局方式,因此我们的项目尽量采用flex+rem的方式进行布局和完成移动端的适配。

3.rem适配原理 rem是相对长度单位,可以做到一样的取值,在不同尺寸的屏幕上的大小按比例缩放。 **rem的定义:**rem(font size of the root element)是相对于根元素(即html元素)font-size计算值的倍数。 例如html标签设置font-size:16px,同时div设置width:1.2rem。那么这个div的宽度就是1.2rem=16px*1.2=19.2px。 因此这种方法的适配原理是:根据不同屏幕的宽度,以相同的比例动态修改html的font-size适配,并将px替换成rem,它可以很好的根据根元素的字体大小来进行变化,从而达到各种屏幕基本一直的效果体验。

4.JS控制适配屏幕 明白了REM的原理后,我们就可以使用这个特点来进行适应布局了,这也是现在比较主流的移动端web适配方案。

//获得屏幕大小 let htmlwidth = document.documentElement.clientWidth || document.body.clientWidth; //浏览器兼容 console.log("屏幕宽度:"+htmlwidth) //iphone5:320 iphone6:375

//获得html DOM元素 let htmlDom = document.getElementsByTagName('html')[0];

//给DOM元素设置样式 htmlDom.style.fontSize = htmlwidth/20 + 'px';
//以iphone5为基础 iphone5默认字体大小为16px 320/16=20 即1rem字体大小是屏幕宽度的1/20

已知 fn 为一个预定义函数,实现函数 curryIt,调用之后满足如下条件: 1、返回一个函数 a,a 的 length 属性值为 1(即显式声明 a 接收一个参数) 2、调用 a 之后,返回一个函数 b, b 的 length 属性值为 1 3、调用 b 之后,返回一个函数 c, c 的 length 属性值为 1 4、调用 c 之后,返回的结果与调用 fn 的返回值一致 5、fn 的参数依次为函数 a, b, c 的调用参数 输入 var fn = function (a, b, c) {return a + b + c}; curryIt(fn)(1)(2)(3); 输出 6

function curryIt(fn) { //获取fn参数的数量 var n = fn.length; //声明一个数组args var args = []; //返回一个匿名函数 return function(arg){ //将curryIt后面括号中的参数放入数组 args.push(arg); //如果args中的参数个数小于fn函数的参数个数, //则执行arguments.callee(其作用是引用当前正在执行的函数,这里是返回的当前匿名函数)。 //否则,返回fn的调用结果 if(args.length < n){ return arguments.callee; }else return fn.apply("",args); } }

递增和递减操作符 ++ --

在应用于包含有效数字的字符串时,先将其转换为数字值,再执行加减1的操作。字符串变量变成数值变量。 在应用于一个不包含有效数字字符的字符串时,将变量的值设置为NaN。字符串变量变成数值变量。 在应用于布尔值 false时,先将其转换为0再执行加减1的操作。布尔值变量变成数值变量 在应用于布尔值 true时,先将其转换为1再执行加减1的操作。布尔值变量变成数值变量 在应用于数值(整数和浮点数)时,执行加减 1 的操作。 在应用于对象时,先调用对象的valueOf()方法以取得一个可供操作的值。然后对该值应用前述规则。如果结果是NaN,则在调用toString()方法后再应用前述规则。对象变量变成数值变量。 对于变量值为undefined,++/-- 输出NaN 对于变量值为null,++/-- null转换为0,再执行加减1操作

var s1 = "2"; var s2 = "z"; var b = false; var f = 1.1; var o = { valueOf: function() { return -1; } }; var u; var n = null; s1++; // 值变成数值3 数据类型由string变成为number s2++; // 值变成NaN 数据类型由string变成为number b++; // 值变成数值1 数据类型由Boolean变成为number f--; // 值变成 0.10000000000000009(由于浮点舍入错误所致) 数据类型number不改变 o--; // 值变成数值-2 数据类型由object变成为number u++; // 值变成NaN 数据类型由undefined变成为number n++; // 值变成1 数据类型由null变成为number

js错误捕获

1.可疑区域增加 Try-Catch 2.全局监控 JS 异常 window.onerror 3.全局监控静态资源异常 window.addEventListener 4.捕获没有 Catch 的 Promise 异常:unhandledrejection 5.VUE errorHandler 和 React componentDidCatch 6.监控网页崩溃:window 对象的 load 和 beforeunload 7.跨域 crossOrigin 解决

window.onerror和window.addEventListener('error') 相同点是都可以捕获到window上的js运行时错误。区别是1.捕获到的错误参数不同 2.window.addEventListener('error')可以捕获资源加载错误,但是window.onerror不能捕获到资源加载错误

虚拟DOM

https://juejin.im/post/6844903895467032589

JS 操作真实 DOM 的代价? 用我们传统的开发模式,原生 JS 或 JQ 操作 DOM 时,浏览器会从构建 DOM 树开始从头到尾执行一遍流程。在一次操作中,我需要更新 10 个 DOM 节点,浏览器收到第一个 DOM 请求后并不知道还有 9 次更新操作,因此会马上执行流程,最终执行10 次。例如,第一次计算完,紧接着下一个 DOM 更新请求,这个节点的坐标值就变了,前一次计算为无用功。计算 DOM 节点坐标值等都是白白浪费的性能。即使计算机硬件一直在迭代更新,操作 DOM 的代价仍旧是昂贵的,频繁操作还是会出现页面卡顿,影响用户体验

​ 虚拟 DOM 就是为了解决浏览器性能问题而被设计出来的。如前,若一次操作中有 10 次更新 DOM 的动作,虚拟 DOM 不会立即操作 DOM,而是将这 10 次更新的 diff 内容保存到本地一个 JS 对象中,最终将这个 JS 对象一次性 attch 到 DOM 树上,再进行后续操作,避免大量无谓的计算量。所以,用 JS 对象模拟 DOM 节点的好处是,页面的更新可以先全部反映在 JS 对象(虚拟 DOM )上,操作内存中的 JS 对象的速度显然要更快,等更新完成后,再将最终的 JS 对象映射成真实的 DOM,交由浏览器去绘制。

diff 算法用来比较两棵 Virtual DOM 树的差异,如果需要两棵树的完全比较,那么 diff 算法的时间复杂度为O(n^3)。但是在前端当中,你很少会跨越层级地移动 DOM 元素,所以 Virtual DOM 只会对同一个层级的元素进行对比,如下图所示, div 只会和同一层级的 div 对比,第二层级的只会跟第二层级对比,这样算法复杂度就可以达到 O(n)。 avatar

关于O(n^3)怎么计算出来的

问题描述 原问题标题“React 和 Vue 的 diff 时间复杂度从 O(n^3) 优化到 O(n) ,那么 O(n^3) 和 O(n) 是如何计算出来的? ”

这里的n指的是页面的VDOM节点数,这个不太严谨。如果更严谨一点,我们应该应该假设 变化之前的节点数为m,变化之后的节点数为n。

React 和 Vue 做优化的前提是“放弃了最优解“,本质上是一种权衡,有利有弊。

倘若这个算法用到别的行业,比如医药行业,肯定是不行的,为什么?

React 和 Vue 做的假设是:

检测VDOM的变化只发生在同一层 检测VDOM的变化依赖于用户指定的key 如果变化发生在不同层或者同样的元素用户指定了不同的key或者不同元素用户指定同样的key, React 和 Vue都不会检测到,就会发生莫名其妙的问题。

但是React 认为, 前端碰到上面的第一种情况概率很小,第二种情况又可以通过提示用户,让用户去解决,因此 这个取舍是值得的。 没有牺牲空间复杂度,却换来了在大多数情况下时间上的巨大提升。 明智的选择!

基本概念 首先大家要有个基本概念。

其实这是一个典型的最小编辑距离的问题,相关算法有很多,比如Git中 ,提交之前会进行一次对象的diff操作,就是用的这个最小距离编辑算法。

leetcode 有原题目, 如果想明白这个O(n^3), 可以先看下这个。

对于树,我们也是一样的,我们定义三种操作,用来将一棵树转化为另外一棵树:

删除:删除一个节点,将它的children交给它的父节点

插入:在children中 插入一个节点

修改:修改节点的值

事实上,从一棵树转化为另外一棵树,我们有很多方式,我们要找到最少的。

直观的方式是用动态规划,通过这种记忆化搜索减少时间复杂度。

算法 由于树是一种递归的数据结构,因此最简单的树的比较算法是递归处理。

详细描述这个算法可以写一篇很长的论文,这里不赘述。 大家想看代码的,这里有一份 我希望没有吓到你。

确切地说,树的最小距离编辑算法的时间复杂度是O(n^2m(1+logmn)), 我们假设m 与 n 同阶, 就会变成 O(n^3)。

执行上下文

当 JS 引擎解析到可执行代码片段(通常是函数调用阶段)的时候,就会先做一些执行前的准备工作,这个 “准备工作”,就叫做 "执行上下文(execution context 简称 EC)" 或者也可以叫做执行环境。 执行上下文 为我们的可执行代码块提供了执行前的必要准备工作,例如变量对象的定义、作用域链的扩展、提供调用者的对象引用等信息。 https://juejin.im/post/6844903682283143181#heading-1