记录下我的学习笔记
啊c这是什么东西听起来好厉害 不说都不知道,这是通过``定义的字符串带有的特性,我们光知道
`${}`这种用法,却很少有人知道下边这种用法
`\n`.length //1
String.raw`\n`.length //2
var a=function(e){
e+="***"
return e
}
a`\n` //4
当我们不带括号使用函数,函数调用正常
上边的raw函数实际上是ES6内置的标签函数:String.raw(),返回反引号中未处理的文本,不会处理任何反斜杠转义。 出于好奇,我试着如下调用了这个标签函数:
String.raw(`\n`)
String.raw("\n")

raw()函数只接收无括号调用的方式
JavaScript是有编译器的,我蛮怀疑人生的听到这个
L、R代表左侧和右侧,什么玩意儿的左侧和右侧呢?
是一个赋值操作的左侧和右侧
LHS查询和RHS查询的作用场景是编译器在编译过程的第二部产生了代码,引擎执行它时,会通过查找变量来判断它是否已声明过;
而LHS和RHS查询的区别在哪,按照我的理解:
当我们进行赋值语句操作的时候,就是LHS引用;当我们如这样直接使用变量console.log(a),是RHS引用。
RHS可以理解成retrieve his source value(取得他的源值)。
举个例子
console.log(a)
这里的a是RHS引用,因为这里并没有赋予任何值 与之相反的是这句
a=2
那么这里是LHS引用,我们不关心当前值是多少,我们也不太需要知道当前值,只需要为a赋值就行。
对于这一块暂时不继续深入,标记一下有机会再说
Js是非常灵活的语言,null或undefined是我觉得最灵性的设定。不过我们如果使用.或者[]访问它们,会报错TypeError,而有时候(大部分时候)我们不希望做一次额外的类型判断
实际场景中,应该有不少老哥遇到过由于undefined.xxx导致的TypeError,而ES2020增加了?.属性访问表达式可以帮我们简化这个问题

所以啦,得意于?.
我们在下述这种代码中可以直接条件判断
if(res?.code===1){
//do something success
}
//如果不用res,在res不标准的情况下,res若可能为null,应该这么写
if(res&&res.code===1){
//do something success
}
对于专注于偷懒的程序员,这是个相当不错的特性
众所周知,js里传一个函数是很常见的操作,调用一个传入的函数还要判断是不是空的麻不麻烦呀?所有可以试试和上边条件式属性访问类似的条件式调用
举个例子,这是用来显示同步方法运行时间的简单函数,需要传入一个函数
fun=function(func){
let time0=new Date().getTime();
func();
console.log(new Date().getTime()-time0)
}
如果我们直接运行它,那自然是报错

如果我们使用条件式调用,那么一切正常

不过注意一点,这不代表我们可以传入任意值,非null或者undefined还是会报错

之前做项目的时候,出现过上线版本的log过多的情况,当时参考网上大家的操作出现过在globalThis绑定一个自定义的log函数,但是说实在的用得非常不习惯,后来自己琢磨了一下是不是可以通过重写log来屏,结果正是可行的。
console.log=()=>{}
//类似的eval其实也可以屏蔽
eval=()=>{}
根据官方定义,??求值先定义的操作数,如果其左操作数不是null或undefined,就返回该值,??只会在第一个操作数求值为null或者undefined时才会求第二个值
ps:有没有发现和||很像? 其实还是有区别的,不知道有没有老哥踩过类似下边这种代码的坑:
let max = maxWidth || preferences.maxWidth || 500
当我们认为maxWidth为0是有效的情况下,使用||是不合适的,因为JavaScript解释器把0转换为假而不是真,这句话可能会一直跑到500,而且永远不会是0
使用??则不同,0、空字符串、false都将作为真值,因为它不是undefined或者null
debugger断点,使用该语句会形成一个断点,执行中的JavaScript会停止,我们此时可以用调试器打印变量、检查调用栈类似的。我尝试了一下,是意外之喜没错了。


这种创建对象的形式和正常创建有什么区别?

这就涉及原型的概念,说原型你可能不熟悉,但是prototype你应该听过。在JavaScript中Date、Array这些内置类(然而往底下纠的话,原来这些都是构造函数,震撼我一整年)你也肯定耳熟能详。如果你还不知道什么的原型,往下走看一下"什么的原型链"然后回来看看。 Object.create()的强悍之处在于,它可以以任意原型创建一个新的对象,
比如这样
Object.create({head:'Hello',body:'prototype'})

或者这样
Object.create(null)

可以看到我们用null作为参数时,打印出来直接就没有属性,这意味着这个Object是完完全全空的,它没有在Object这里继承任何东西;
而如果我们要创建一个普通的空对象(常规定义方法是{})
可以这样
Object.create(Object.prototype)

同样的,我们创建一个Array也可以用Object.create():

我们使用Date、Object的时候,其实使用的类似于new Array()这样的语句创建的对象就是以Array.prototype为原型,通过new Date()创建的对象也是以Date.prototype为原型。我们尝试在控制台打印一下Date.prototype:

下边就和原型链扯上关系咯
这里看到constructor,显然这是构造函数,我们尝试展开:

可以看到这里还有一个prototype,这个prototype指向Date本身

可以看到这里还有一个[[Prototype]],这玩意儿我们也展开,就会发现看起来有点熟悉是不是?好家伙这是Function (注意[[Prototype]]其实就是__proto__)
首先,定义上,__proto__是叫做隐式原型,prototype则叫做显示原型
而它们之间的关系如下:
__proto__的值为其对应构造函数(对应类)的prototype的值fn=()=>{}
fn.__proto__ === Function.prototype

__proto__属性是创建对象时自动添加的,默认值为其构造函数的prototype;我们上边也试着看了一下Date的prototype,我现在想着是整条原型链出来,首先放张经典的原型链继承关系图,我几乎每篇文章都能看到这个

我们怎么样才能整出条自己的原型继承,其实保持下边两个准则就行:
__proto__属性指向父亲的prototype属性prototype属性的constructor属性指向其本身然后同样是这个经典的例子:
function Son(){}
function Father(){}
Son.prototype = new Father();
Son.prototype.__proto__===Father.prototype //true
我们发现,其实只要满足Son.prototype.__proto__===Father.prototype,那么继承关系成立。
不过有些值得注意的是:
Object.prototype.__proto__===null__proto__属性指向其构造函数的prototype(这是显然的)今天暂且到这里,原型链一块要注意区分__proto__和prototype的区别,总结起来的两点是