1、==、===、Object.is()、SameValue、SameValueZero的区别
==
:等同,比较运算符,两边值类型不同的时候,先进行类型转换,再比较;===
:恒等,严格比较运算符,不做类型转换,类型不同就是不等;- 如果类型不同,就不相等
- 如果两个都是数值,并且是同一个值,那么相等;
- 值得注意的是,如果两个值中至少一个是NaN,那么不相等(判断一个值是否是NaN,可以用
isNaN()
或Object.is()
来判断)。
- 值得注意的是,如果两个值中至少一个是NaN,那么不相等(判断一个值是否是NaN,可以用
- 如果两个都是字符串,每个位置的字符都一样,那么相等;否则不相等。
- 如果两个值都是同样的Boolean值,那么相等。
- 如果两个值都引用同一个对象或函数,那么相等,即两个对象的物理地址也必须保持一致;否则不相等。
- 如果两个值都是null,或者都是undefined,那么相等。
Object.is()
是ES6新增的用来比较两个值是否严格相等的方法,内部采用的比较算法就是SameValue(x, y),与===
的行为基本一致。-
举个栗子☺:
+0 === -0 //true NaN === NaN // false Object.is(+0, -0) // false Object.is(NaN, NaN) // true
- +0不等于-0。
- NaN等于自身。
-
4. SameValue
与 SameValueZero
其实是 ecma
中,比较值是否相等的算法。
举个例子,我们都知道 NaN
的定义是与任何其他值均不相等。
因此,就会有以下结果:
NaN == NaN // false NaN === NaN // false
有的时候,我们还是希望可以判断 NaN
,因此就诞生了 SameValue
:
SameValue(NaN, NaN); // true
对于 +0
与 -0
则会认为是不同的值:
+0 == -0 // true +0 === -0 // true SameValue(+0, -0); // false
以上两种情况,就是 SameValue
与 ===
不同的地方。(判断 NaN
,+0
,-0
时)
使用 SameValueZero
的话,对于 +0
与 -0
则会认为是同一个值,includes内部使用的比较算法就是SameValueZero:
+0 == -0 // true +0 === -0 // true SameValue(+0, -0); // false SameValueZero(+0, -0); // true
所以, SameValueZero
与 ===
只有一个不是的情况。
2、JavaScript中属性和特性(属性描述符)
对象的本质:ECMA-262把对象定义为:无序属性的集合,其属性可以包含基本值、对象或者函数。
属性:数据属性、访问器属性
属性特性(属性描述符):
数据属性:
- [[Configurable]]:默认值为true,a、表示能否通过delete删除属性从而重新定义属性 b、能否修改属性的特性 c、能够把属性由数据属性修改为访问器属性
- [[Enumerable]]:默认值为true,表示能否通过for-in循环返回该属性(所以:如果为false,那么for-in循环没法枚举它所在的属性)
- [[Writable]]:默认值为true,表示能否修改属性的值,这是与[[Configurable]]不同之处。
- [[Value]]:默认值为undefined,这个值即为属性的属性值,我们可以在这个位置上读取属性值,也可以在这个位置上写入属性值。
- 注意:上述的默认是指通过构造函数或对象字面量创建的对象所自身拥有的属性,而通过调用Object.defineProperty()方法创建的属性,其前三个特性的默认值均为false
- 一旦把属性设置成为不可配置的Configurable:false,就不能再把它变回可配置了。
访问器属性:
- [[Configurable]]:默认值为true,a、表示能否通过delete删除属性从而重新定义属性 b、能否修改属性的特性 c、能够把属性由访问器属性修改为数据属性
- [[Enumerable]]:默认值为true,表示能否通过for-in循环返回该属性(所以:如果为false,那么for-in循环没法枚举它所在的属性)
- [[Get]]:在读取属性时调用的函数。默认值为undefined 关键:特性可以是一个函数
- [[Set]]: 在写入属性时调用的函数。默认值为undefined 关键:特性可以是一个函数 由于get和set函数也属于属性的特性,那么他们就有可能(说有可能是因为这两个函数也不是必须的)出现在Object.defineproperty的第三个参数描述符对象的属性中。
-
注意:1.相对于数据属性,我们发现访问器属性中没有writable特性和value特性。这是因为访问器属性不包含数据值,那么我们怎么当然就不可修改属性的值(用不到writable特性),更不用考虑value了。
2.访问器属性不能直接定义,必须是用Object.defineProperty()来定义。(通过这个规定我们就能准确地判断出访问器属性和数据属性了)
修改属性特性(属性描述符):
Object.defineProperty()
Object.defineProperties()方法定义多个特性
Object.getOwnPropertyDescripter()方法读取属性的描述符
另外:
hasOwnProperty() //判断属性是否是存在于自己的实例中,如果是:返回true,如果仅仅存在自己的原型总,则返回false
Object.getOwnPropertyName() //如果你想要获得所有实例属性,无论他是否可枚举,可以使用这个方法
Object.keys() //如果你想要获得所有实例属性,并仅需可枚举的,可以使用这个方法
for in //主要用于遍历对象的可枚举属性,包括自有属性、继承自原型的属性
class类中,类的内部所有定义的方法,都是不可枚举的(non-enumerable)。
class Point { constructor(x, y) { // ... } toString() { // ... } } Object.keys(Point.prototype) // [] Object.getOwnPropertyNames(Point.prototype) // ["constructor","toString"]
ES5中通过添加到原型链上的属性是可以枚举的
var Point = function (x, y) { // ... }; Point.prototype.toString = function() { // ... }; Object.keys(Point.prototype) // ["toString"] Object.getOwnPropertyNames(Point.prototype) // ["constructor","toString"]
Object.getPrototype(obj)
是ES5中用来获取obj对象的原型对象的标准方法。
3、ES6 的 class 和构造函数的区别
为什么ES5无法继承原生构造函数? - 简书 (jianshu.com)
1. 严格模式
类和模块的内部,默认就是严格模式,所以不需要使用use strict
指定运行模式。只要你的代码写在类或模块之中,就只有严格模式可用。考虑到未来所有的代码,其实都是运行在模块之中,所以 ES6 实际上把整个语言升级到了严格模式。
2. 不存在提升
类不存在变量提升(hoist),这一点与 ES5 完全不同。
3. 方法默认是不可枚举的
ES6 中的 class,它的方法(包括静态方法和实例方法)默认是不可枚举的,而构造函数默认是可枚举的。细想一下,这其实是个优化,让你在遍历时候,不需要再判断 hasOwnProperty 了
4. class 的所有方法(包括静态方法和实例方法)都没有原型对象 prototype,所以也没有[[construct]],不能使用 new 来调用。
5. class 必须使用 new 调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用 new 也可以执行。
6. ES5 和 ES6 子类 this 生成顺序不同,es5的写法不能继承原生构造函数(比如Array、Number等)
原生构造函数会忽略apply方法传入的this,也就是说,原生构造函数的this无法绑定,导致拿不到内部属性。ES5 是先新建子类的实例对象this,再将父类的属性添加到子类上,由于父类的内部属性无法获取,导致无法继承原生的构造函数。
ES6 是先新建父类的实例对象this,然后再用子类的构造函数修饰this,使得父类的所有行为都可以继承。因此class可以继承原生构造函数并自定义原生构造函数的子类。
例外
ES6 改变了Object构造函数的行为,一旦发现Object方法不是通过new Object()这种形式调用,ES6 规定Object构造函数会忽略参数。
class Obj extends Object{
constructor(){
super(...arguments)
}
}
var o = new Obj({attr:true});
console.log(o.attr); //输出 undefined
** 原生构造函数大致有下面这些:
- List item
- Boolean()
- Number()
- String()
- Array()
- Date()
- Function()
- RegExp()
- Error()
- Object()
7. ES6可以继承静态方法,而构造函数不能
4、js创建对象的方法及区别
JavaScript中创建一个对象可以采用3种方式:new Object()
,Object.create()
,或字面量写法。
new Object()
new Object()这种方式即我们常说的“使用构造函数创建对象”,new运算符实际做了以下4件事情:
1.创建一个新的对象
2.链接到原型(将构造函数的 prototype
赋值给新对象的 __proto__
);
3.将构造函数的作用域赋给新对象(因此this指向了这个新对象)
4.执行构造函数中的代码(为这个新对象添加属性)
4.返回新对象。如果是引用类型就返回引用类型的对象。(如果没有返回对象类型object包括Functoin, Array, Date, RegExg, Error),那么new表达式中的函数调用将返回该对象的引用)
new Object() 与Object.create()的区别:
1)创建对象的方式不同
new Object() 通过构造函数来创建对象, 添加的属性是在自身实例下。
Object.create() es6创建对象的另一种方式,可以理解为继承一个对象, 添加的属性是在原型下
2)创建对象属性的性质不同
Object.create() 用第二个参数来创建非空对象的属性描述符默认是为false的,而构造函数或字面量方法创建的对象属性的描述符默认为true。
3)创建空对象时不同
当用构造函数或对象字面量方法创建空对象时,对象时有原型属性的,即有_proto_;
- 字面量和
new
关键字创建的对象是Object
的实例,原型指向Object.prototype
,继承内置对象Object
当用Object.create()方法创建空对象时,对象是没有原型属性的。
Object.create(arg, pro)
创建的对象的原型取决于arg
,arg
为null
,新对象是空对象,没有原型,不继承任何对象;arg
为指定对象,新对象的原型指向指定对象,继承指定对象
5、async await 和promise
promise:1、对象的状态不受外界影响。2、一旦状态改变,就会凝固,不能再次改变
Promise是一个立即执行函数,但是他的成功(或失败:reject)的回调函数resolve却是一个异步执行的回调。当执行到resolve()时,这个任务会被放入到回调队列中,等待调用栈有空闲时事件循环再来取走它。
async 定义的函数,返回的其实是一个Promise对象
await 操作符后面的表达式是一个Promise的时候,它的返回值,实际上就是Promise的回调函数resolve的参数。
Promise.resolve()将现有对象快速转换成promise对象
let p = new Promise((resolve,reject) => {
setTimeout(() => {
resolve('success');
},500);
});
let pp = Promise.resolve(p);
pp.then(result => {
console.log(result);
});
console.log(pp == p);
Promise.reject() 快速获取一个拒绝状态的promise对象
Promise.all()同时执行多个promise >> 当所有状态都是成功状态才返回数组,只要其中有一个的对象是reject的,就返回reject的状态值。
Promise.race()执行多个promise >> 哪个对象返回的快就返回哪个对象,如果对象其中有reject状态的,且返回的够快,就能被catch捕捉到。race最终返回的只有一个值
**Pormise内部的错误外界用try-catch捕捉不到
联系:
区别:
1、简洁
Promise虽然一方面解决了callback的回调地狱,但是相对的把回调“纵向发展”了,形成了一个回调链;
Async/Await写法更简洁,更直观。不需要写.then,不需要写匿名函数处理Promise的resolve值,也不需要定义多余的data变量,还避免了嵌套代码
2、错误处理:
Async/Await 让 try/catch 可以同时处理同步和异步错误
3、调试:
async/await能够使得代码调试更简单,可以像调试同步代码一样跳过await语句。
在Promise中, 我们需要使用 .catch,这样错误处理代码非常冗余
Promise调试
《1》不能在返回表达式的箭头函数中设置断点
《2》如果你在.then代码块中设置断点,使用Step Over快捷键,调试器不会跳到下一个.then,因为它只会跳过异步代码。
4、错误栈
Promise链中返回的错误栈没有给出错误发生位置的线索。错误栈中唯一的函数名为callAPromise,然而它和错误没有关系。(文件名和行号还是有用的)。
async/await中的错误栈会指向错误所在的函数。分析生产环境的错误日志时,它将非常有用。
JavaScript 如何优雅的处理 async/await 异常:
async/await 如何优美的处理异常? - CNode技术社区 (cnodejs.org)
(156条消息) JavaScript 如何优雅的处理 async/await 异常_Jusme_wx的博客-CSDN博客_js async await 异常处理
6、javascript垃圾回收机制
引用计数垃圾收集(限制:无法处理循环引用)
这是最初级的垃圾收集算法。此算法把“对象是否不再需要”简化定义为“对象有没有其他对象引用到它”。如果没有引用指向该对象(零引用),对象将被垃圾回收机制回收。
标记-清除算法
这个算法把“对象是否不再需要”简化定义为“对象是否可以获得”。垃圾回收器将定期从根开始,找到所有可以获得的对象和收集所有不能获得的对象。
从2012年起,所有现代浏览器都使用了标记-清除垃圾回收算法。
避免垃圾回收
通过上面内容了解了,浏览器虽然可以自动化执行垃圾回收,但如果项目比较大代码复杂,回收执行代价较大,某些情况甚至不能识别回收
1.数组array优化
将[]赋值给一个数组对象,是清空数组的捷径(例如: arr = [];),但是需要注意的是,这种方式又创建了一个新的空对象,并且将原来的数组对象变成了一小片内存垃圾!实际上,将数组长度赋值为0(arr.length = 0)也能达到清空数组的目的,并且同时能实现数组重用,减少内存垃圾的产生。
2. 对象尽量复用
对象尽量复用,尤其是在循环等地方出现创建新对象,能复用就复用。不用的对象,尽可能设置为null,尽快被垃圾回收掉。
3.循环优化
在循环中的函数表达式,能复用最好放到循环外面。
四种常见的内存泄漏:全局变量,未清除的定时器,闭包,以及 dom 的引用
- 全局变量 不用 var 声明的变量,相当于挂载到 window 对象上。如:b=1; 解决:使用严格模式
- 被遗忘的定时器和回调函数
- 闭包
- 没有清理的 DOM 元素引用
7、var、let、function 和 const-----变量提升和函数提升
JS代码分为两个阶段:编译阶段和执行阶段;变量提升和函数提升发生在编译阶段。
首先我们要知道定义一个JS变量分为三个阶段
- 创建create
- 初始化initialize
- 赋值assign
var、let、function 和 const的过程
- var 的创建和初始化都被提升了。
- function 的创建、初始化和赋值都被提升了。
- let 的创建被提升了,但是初始化和赋值没有提升。
- const的创建被提升但是初始化没提升。const没有赋值。
函数优先:当变量声明和函数声明同时存在时,函数声明优先于变量声明,即函数声明会覆盖变量声明。原因在于函数的赋值过程也会提升。那有人可能有疑问,如果将var变成let呢?结果为foo不允许被重复声明。
foo(); var foo; function foo() { console.log(1) }
let 有暂时死区:所谓暂时死区,就是不能在初始化之前,使用变量。
- let 声明的变量的作用域是块级的;
- let 不能重复声明已存在的变量;
- let 有暂时死区,不会被提升。
const声明一个只读的常量 。一旦声明, 就必须立即初始化,值也不能再改变。
对于简单的基本数据类型(string number boolean null undefined)值保存在指向的那个内存地址,因此等同于常量。
但对于引用数据类型(object array ),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即指向另一个固定的地址)------如果这个地址指向一个对象,不可变的只是这个地址,即不能把foo指向另一个地址,如果将另一个对象赋值给改对象,就会报错。但对象本身是可变的,可以修改其属性。
var、let和 const除了声明和提升的区别还有:
ES5 只有全局作用域和函数作用域,没有块级作用域 ----- var ,这带来很多不合理的场景
缺点:内层变量可能会覆盖外层变量;用来计数的循环变量泄露为全局变量。所以ES6新增了let,const来声明块级作用域
在同一作用域下var可以重复声明,let,const不可以重复声明。
var声明的变量不存在块级作用域, let,const声明的变量存在块级作用域。
var和let可以重新赋值 const声明的是一个常量,const声明的变量必须要进行初始化 不能够重新赋值
var声明的变量不存在暂时性死区,let,const声明的变量存在暂时性死区。
8、setInterval的弊端和解决方案
弊端
1. setInterval对自己调用的代码是否报错漠不关心。即使调用的代码报错了,它依然会持续的调用下去
2. setInterval无视网络延迟。在使用ajax轮询服务器是否有新数据时,必定会有一些人会使用setInterval,然而无论网络状况如何,它都会去一遍又一遍的发送请求,如果网络状况不良,一个请求发出,还没有返回结果,它会坚持不懈的继续发送请求,最后导致的结果就是请求堆积。
3. setInterval并不定时。如果它调用的代码执行的时间大于定时的时间,它会跳过调用,这就导致无法按照你需要的执行次数或无法得到你想要的结果
4. 把浏览器最小化显示等操作时,setInterval并不是不执行程序,
它会把setInterval的回调函数放在队列中,等浏览器窗口再次打开时,一瞬间全部执行时
所以,鉴于这么多但问题,目前一般认为的最佳方案是:用setTimeout模拟setInterval,或者特殊场合直接用requestAnimationFrame
补充:JS高程中有提到,JS引擎会对setInterval进行优化,如果当前事件队列中有setInterval的回调,不会重复添加。
解决方案
使用setTimeout代替setInterval。
可以给setTimeout设置时间后,在最后调用自身。如果希望“匀速”触发。可以计算代码执行时间,用希望的延迟减去上次执行的时间。
注:有一种想法是将setInterval的延迟时间设置的长于上述的几种时间,来达到绝对的均速调用。但事实上,js的计时器因为自身机制的原因,存在4ms–15ms的误差。
//实现的方法挺简单的 ,如下代码
//参数: 毫秒 需要执行的方法
function setInter(s,fn){
let timeOut = (s,fn)=>{
setTimeout(()=>{
fn();
timeOut(s,fn);
},s)
}
timeOut(s,fn);
}
//调用上面的方法
var i=0;
setInter(1000,function(){ i++; console.log("hello world!"+i)})
原文链接:https://blog.csdn.net/runOnWay/article/details/80900437
9、服务端渲染SSR和实现原理
10、 preflight---CORS 预检请求(在非简单请求且跨域的情况下,浏览器会发起options预检请求)
在跨域的情况下,对于可能对服务器数据产生副作用的HTTP请求方法,浏览器必须先使用OPTIONS
方法发起一个空body的预检请求,从而获知服务器是否允许该跨域请求:如果允许,就发送带数据的真实请求;如果不允许,则阻止发送带数据的真实请求。
HTTP请求包括: 简单请求 和 需预检的请求
1 简单请求
简单请求需满足以下两个条件
- 请求方法是以下三种方法之一:
- HEAD
- GET
- POST
- HTTP 的头信息不超出以下几种字段
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type: 只限于 (application/x-www-form-urlencoded、multipart/form-data、text/plain)
2 复杂请求
非简单请求即是复杂请求
常见的复杂请求有:
-
请求方法为 PUT 或 DELETE
-
Content-Type 字段类型为 application/json
-
添加额外的http header 比如access_token
11、 instanceof和isPrototypeOf的区别
isPrototypeOf()
方法用于测试一个对象是否存在于另一个对象的原型链上。
instanceof
运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype
属性,是一个运算符,它期望两个操作数,一个对象和一个Constructor function,它将测试传递的函数原型属性是否存在于对象的链上(通过[[HasInstance]](V)
内部操作,仅在函数对象中可用)。
描述:instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上。 // 定义构造函数 function C(){} function D(){} var o = new C(); // true,因为 Object.getPrototypeOf(o) === C.prototype
12、在浏览器中使用javascript module
在浏览器中使用javascript module(译) - 简书
13、promise有几种状态,什么时候会进入catch?
1)pending:在过程中,没有结果,不会触发then和catch
2)resolved:已经解决,触发then
3)rejected:已经拒绝,触发catch
14、electron ??Introduction | Electron
15、DOMContentLoaded
16、元编程 :JS中无处不在的元编程 - 知乎 (zhihu.com)
Reflect - JavaScript | MDN (mozilla.org)
Proxy - JavaScript | MDN (mozilla.org)
17、高阶函数 和 高阶组件 -----React——高阶组件(HOC)
高阶函数:一个函数内部返回一个新的函数,内部采用闭包的写法
高阶组件:(HOC) Higher-Order-Component
为了提⾼组件复⽤率,可测试性,就要保证组件功能单⼀性;但是 若要满⾜复杂需求就要扩展功能单⼀的组件,在React⾥就有了 HOC(Higher-Order Components)的概念,
定义:⾼阶组件是⼀个⼯⼚函数,它接收⼀个组件并返回另⼀个组件。简单来说就是组件复用
18、Refresh token的意义:关于 token 过期的疑惑,为什么需要 refresh token? - 糯米PHP (nuomiphp.com)
流程:
用户每次登录会获取 access token 和 refresh token
前端保存 access token 和 refresh token
每次前端请求带上 access token ,后端用 access token 去 oauth2 服务验证是否有效。
如果无效则反馈给前端,前端会再次用 refresh token 发送请求。oauth2 会验证 refresh token 是否在有效期内。如果有效,则返回新的 access token 和 refresh token (延长有效期)。前端更新存储的 token 。
如果 refresh token 无效则跳转到登录页。
refresh token 设置了一周时间。如果用户每天用,其实基本不会要求重登录。只有长期不用才会要求重新登录。
存在意义:
因为 HTTP1.1 协议每次请求都是独立的,不能复用的连接。所以,在每次请求中都需要带上令牌。令牌有效期越长,那么请求携带次数就越多,传输过程越容易暴露令牌,导致安全性下降。总而言之,就是一个令牌使用时间和次数越多,那么在使用过程中越不安全。反之,把令牌有效期设置的越短,那么就越安全。但如果这样子,将导致用户体验很差。那么如何解决这个矛盾的?对于经典的 web 应用,令牌的有效时间是 30 分钟,在有效期内使用令牌请求,后端将刷新令牌的有效期。如果超过 30 分钟再发起请求,服务端会要求前端带上 refresh token ,即刷新令牌。刷新令牌有效期可以设置很多天,比如设置一周。刷新令牌设置这么长时间那不是很不安全?其实不是的,刷新令牌相比前面每次请求使用的令牌来说,区别在于低频次使用。虽然有效期长得多,但使用次数非常低,有的方案只用一次。如前所述,使用刷新令牌换取普通令牌,并获取一个新的刷新令牌,原刷新令牌作废。如此,便实现了用户登录后,只要不超过一周内访问服务,那便可以一直免登录(例如你手机上的微信)。最后关于令牌本身,可以是服务端随机生成的 session id ,也可以是服务端不保存映射关系的 JWT ,主要看具体应用场景。
最后归纳一下,token 和 refresh token 的区别在于有效期一个短一个长。使用上 token 用于每次请求,refresh token 用于 token 过期后去换取新的 token 和 refresh token 。这样设计的目的,就是为了解决安全性和使用体验之间的矛盾。
access token 有效期短 被盗损失更小 安全性更高
如果refresh token被盗了 想刷新access token的话 也需要提供过期的refresh token 盗取难度增加
同时refresh token只有在第一次获取和刷新access token时才会在网络中传输,因此被盗的风险远小于access token 从而在一定程度上 更安全了一点
18、JWT和普通Token的区别
https://blog.csdn.net/qq_41369135/article/details/116530607
一、传统token
例子:传统token登陆过程
前端点击登陆,服务器验证账号密码成功
服务器生成令牌,本质是一个32位的uuid
将该令牌存到数据库或redis中,key是uuid,value是userId
把令牌返给客户端,客户端把令牌存在cookie中。
下次请求的时候就把令牌放在请求头里带上
从redis中验证该令牌是否过期
获取value内容userId
根据userId查询用户信息,再返回客户端
传统token优缺点
优点:
可以隐藏真实数据
适用于分布式/微服务
安全系数高
缺点:
存放在redis,必须依赖服务器,暂用服务器资源
效率非常低
二、JWT
例子:JWT登陆过程
Jwt优缺点有那些
优点:
无需服务器端存放数据,减轻服务器端的压力
占用带宽比较小、跨语言
token自身包含用户信息且无法篡改,在服务(网关)中可以自行解析校验出用户信息,对认证服务器(account-svc)压力小
缺点:
建议不要放铭感数据 userid、手机号码(如果非要放userId,deptId等信息,可采用rsa256算法加密)RSA256生成Jwt
Jwt生成之后无法修改(发生变化)
后端无法统计 生成jwt
无法吊销令牌,只能等待令牌自身过期
令牌长度与其包含用户信息多少正相关,传输开销较大
Jwt是无状态的,如果别人获取到了,别人也能用
19、Token、session、cookie
20、手写观察者模式和发布订阅模式
21、前端工程化和部署(husky、sonar、github/gitlab/jekins/阿里云/腾讯云等自动化部署)、react佳实践
22、前端权限管理
面试官:vue要做权限管理该怎么做?如果控制到按钮级别的权限怎么做? | web前端面试 - 面试官系列 (vue3js.cn)
24、npm vs yarn:npm 和 yarn 那个好用? - 知乎 (zhihu.com)
26、CSS选择器的解析顺序--从右向左
(130条消息) 为什么CSS选择器是从右往左解析_津泊客的博客-CSDN博客_css解析顺序
27、CDN缓存
【CDN 最佳实践】CDN缓存策略解读和配置策略 - 知乎 (zhihu.com)
28、git rebase vs. git merge
git merge:按照提交时间的先后顺序进行依次放到master分支上
git merge --no-ff
默认情况下,Git执行"快进式合并"(fast-forward merge),会直接将develop分支指向feature分支。使用--no-ff参数后,会执行正常合并,在develop分支上生成一个新节点。为了保证版本演进的清晰,推荐采用这种做法
git rebase:实际上是将当前执行rebase分支的所有基于原分支提交点之后的commit重新生成一个新的commit hash值,再次基于原分支目前最新的commit点上进行提交,不再根据两个分支上实际的每次提交的时间点排序,rebase完成后,重新合并的代码的commit呈线性排列
git merge 及 git rebase的区别 - coolw - 博客园 (cnblogs.com)
git pull和git pull --rebase区别:git pull做了两个操作分别是‘获取’和合并。所以加了rebase就是以rebase的方式进行合并分支得到一条干净的分支流。
git pull = git fetch + git merge FETCH_HEAD
git pull --rebase = git fetch + git rebase FETCH_HEAD
-
git rebase -i dev 可以将dev分支合并到当前分支
这里的”-i“是指交互模式。就是说你可以干预rebase这个事务的过程,包括设置commit message,暂停commit等等。 -
git rebase --abort 放弃一次合并
-
合并多次commit操作:
1 git rebase -i dev
2 修改最后几次commit记录中的pick 为squash
3 保存退出,弹出修改文件,修改commit记录再次保存退出(删除多余的change-id 只保留一个)
4 git add .
5 git rebase --continue
6 git push origin 开发分支
Merge vs Rebase - 知乎 (zhihu.com)
(130条消息) git git pull和git pull --rebase的区别_神奇大叔的博客-CSDN博客_git pull与git rebase
29、git常用命令
Stage & Commit Files: git add, git commit, & git log free tutorial (nobledesktop.com)
a、Check Status
git status:check the status of our Git repo
b、Stage Files to Prepare for Commit
- Stage all files: git add .
- Stage a file: git add example.html (replace example.html with your file name)
- Stage a folder: git add myfolder (replace myfolder with your folder path)
Keep in Mind:
- If the file name/path has a space, wrap it in quotes.
- You can repeat the above commands for different files and folders.
c、 Unstage a File
git reset HEAD example.html (replace example.html with your file or folder)
d、Deleting Files
要删除的文件是没有修改过的,就是说和当前版本库文件的内容相同
- git rm example.html to remove a file (and stage it)
- git rm -r myfolder to remove a folder (and stage it)
要删除的文件已经修改过,就是说和当前版本库文件的内容不同。
-
git rm -f example.html to force removal a file (and stage it)
当我们需要删除暂存区
或分支
上的文件, 但本地又需要使用, 只是不希望这个文件被版本控制, 可以使用
git rm --cached file_path
e、Commit Files
git commit -m "Message that describes what this change does"
f、Fixing Your Last Commit Message
git commit --amend -m "Put your corrected message here"
g、View a List of Commits
- To see a simplified list of commits, run this command: git log --oneline
- To see a list of commits with more detail (such who made the commit and when), run this command: git log
- To see a list of commits with even more detail (including which files changed), run this command: git log --stat
git reflog show命令,可以查看完整的提交历史,
NOTE: If the list is long, use the Down/Up Arrow keys to scroll and hit Q to quit.
h、git reset --soft 版本号
用于版本的回退,只进行对commit操作的回退,不影响工作区的文件。
git reset -–hard:彻底回退到某个版本,本地的源码也会变为上一个版本的内容,撤销的commit中所包含的更改被冲掉;
i、git checkout -- file命令,来清空工作区的修改
j、git revert
- git revert HEAD 撤销前一次 commit
- git revert HEAD^ 撤销前前一次 commit
git revert -n commit-id
反做多个commit-id
git revert -n commit-idA..commit-idB
- 反做commit-idA到commit-idB之间的所有commit
- 注意:使用-n是应为revert后,需要重新提交一个commit信息,然后在推送。如果不使用-n,指令后会弹出编辑器用于编辑提交信息
合并冲突后退出
git revert --abort
- 当前的操作会回到指令执行之前的样子,相当于啥也没有干,回到原始的状态
合并后退出,但是保留变化
git revert --quit
- 该指令会保留指令执行后的车祸现场
合并后解决冲突,继续操作
- 如果遇到冲突可以修改冲突,然后重新提交相关信息
git add .
git commit -m "提交的信息"
- git reset 是回滚到对应的commit-id,相当于是删除了commit-id以后的所有的提交,并且不会产生新的commit-id记录,如果要推送到远程服务器的话,需要强制推送-f
- git revert 是生成一个新的 commit,将指定的 commit 内容从当前分支上撤除,本身不会对其他的提交commit-id产生影响,如果要推送到远程服务器的话,就是普通的操作git push就好了
- git reset 是把HEAD向后移动了一下,而git revert是HEAD继续前进,只是新的commit的内容和要revert的内容正好相反,能够抵消要被revert的内容
k、
git cherry-pick <commitHash>
将指定的提交(commit)应用于其他分支。
上面命令就会将指定的提交commitHash
,应用于当前分支。这会在当前分支产生一个新的提交,当然它们的哈希值会不一样。
git cherry-pick
命令的参数,不一定是提交的哈希值,分支名也是可以的,表示转移该分支的最新提交。
git cherry-pick feature 表示将feature
分支的最近一次提交,转移到当前分支。
git cherry-pick 教程 - 阮一峰的网络日志 (ruanyifeng.com)
30、css in js vs css module
styled-component,不能预先编译,主要是运行时动态渲染
CSS-in-JS:一个充满争议的技术方案 - 知乎 (zhihu.com)
值得一提的是 @compiled/css-in-js,这个库会用类似于 Angular 的预先(AoT)编译器,将组件样式预先编译为 CSS 字符串,嵌入转译的 JS 代码中。这种方式显著减少了因变量引起的 CSS 冗余问题。
CSS Modules VS. styled-components,哪个才是解决 CSS 不足之处的更好方案? - 掘金 (juejin.cn)
31、 attribute和property的区别
JS中attribute和property的区别 - L_mj - 博客园 (cnblogs.com)
(131条消息) attribute和property,attr()和prop()的区别_代码小公举的博客-CSDN博客
32、为什么将小图片转换成base64格式?
图片的 base64 编码就是可以将一副图片数据编码成一串字符串,使用该字符串代替图像地址;
1. 提升性能: 网页上的每一个图片,都是需要消耗一个 http 请求下载而来的, 图片的下载始终都要向服务器发出请求,要是图片的下载不用向服务器发出请求,base64可以随着 HTML 的下载同时下载到本地.减少https请求。Base64转码后文件比实际的大30%,但是网站启用了GZIP,反而可以忽略增长的大小。Base64在小范围使用上完胜CSS Sprite,因为后者只适用于CSS,而Base64可以直接使用src方式引用,方便快捷。
但也并不是什么图片都适合用base64来处理,因为图片越大,转换的base64的字符串就越长,对带宽的要求更高了。
2. 加密: 让用户一眼看不出图片内容 , 只能看到编码。
3. 方便引用: 在多个文件同时使用某些图片时, 可以把图片转为base64格式的文件, 把样式放在全局中, 比如common.css, 以后在用的时候就可以直接加类名, 二不需要多层找文件路径, 会提升效率
33、webpack对图片和css的处理流程
25、svg转font的好处:SVG转字体图标 - 何菏 - 博客园 (cnblogs.com) ----- iconfont-阿里巴巴矢量图标库