关于前端基础:实用的js-技巧之空值合并运算符gloabalThis

前言ES语法并不是变化无穷的,从最后的ES5曾经到ES12了,理解语言的新个性,能够简化咱们的代码写法或者更高效的实现咱们的诉求,明天次要介绍以下两个罕用的个性: 空值合并运算符、globalThis。 空值合并运算符当遇到某个属性是空值时须要给默认值的操作,来看一下咱们之前的实现: const opt = {}const configValue = opt.value|| 'default value';咱们能够看到应用逻辑或(||)操作符会在左侧操作数为假值时返回右侧操作数,那还有其余实现形式么,就是明天讲的管制合并运算符。 ??(空值合并操作符): 是一个逻辑操作符,当左侧的操作数为 null或者undefined时,返回其右侧操作数,否则返回左侧操作数。 const foo = undefined ?? "foo"const bar = null ?? "bar"console.log(foo) // fooconsole.log(bar) // bar与逻辑或操作符不同,?? 只会在左侧值为 null undefined时才会返回右侧值,如下: const foo = "" ?? 'default string';const foo2 = "" || 'default string';console.log(foo); // ""console.log(foo2); // "default string"具体应用场景能够用于显示后端接口返回数据然而又不确定是否有该字段时,这时候能够应用?? 给个默认值。 gloabalThis以前,从不同的 JavaScript 环境中获取全局对象须要不同的语句: 在 Web 中,能够通过 window、self 取到全局对象;在 Node.js 中,必须应用 global;在涣散模式下,能够在函数中返回 this 来获取全局对象,然而在严格模式和模块环境下,this 会返回 undefined;想要适配不同的环境获取全局对象,通常咱们会写如下的函数: //以前想要获取全局对象,可通过一个全局函数const getGlobal = () => { if (typeof self !== 'undefined') { return self } if (typeof window !== 'undefined') { return window } if (typeof global !== 'undefined') { return global } throw new Error('无奈找到全局对象')}const globals = getGlobal()console.log(globals)在globalThis 呈现之后,咱们能够间接应用 globalThis来获取不同环境下的全局 this 对象(也就是全局对象本身)。 ...

March 24, 2022 · 1 min · jiezi

关于前端基础:JavaScript之类型转换

强制转换强制转换次要指应用Number、String和Boolean三个函数,手动将各种类型的值,散布转换成数字、字符串或者布尔值。 Number()应用Number函数,能够将任意类型的值转化成数值。 上面分成两种状况探讨,一种是参数是原始类型的值,另一种是参数是对象。 (1)原始类型值 原始类型值的转换规则如下。 // 数值:转换后还是原来的值Number(324) // 324// 字符串:如果能够被解析为数值,则转换为相应的数值Number('324') // 324// 字符串:如果不能够被解析为数值,返回 NaNNumber('324abc') // NaN// 空字符串转为0Number('') // 0// 布尔值:true 转成 1,false 转成 0Number(true) // 1Number(false) // 0// undefined:转成 NaNNumber(undefined) // NaN// null:转成0Number(null) // 0Number函数将字符串转为数值,要比parseInt函数严格很多。基本上,只有有一个字符无奈转成数值,整个字符串就会被转为NaN。 parseInt('42 cats') // 42Number('42 cats') // NaN下面代码中,parseInt一一解析字符,而Number函数整体转换字符串的类型。 另外,parseInt和Number函数都会主动过滤一个字符串前导和后缀的空格。 parseInt('\t\v\r12.34\n') // 12Number('\t\v\r12.34\n') // 12.34(2)对象 简略的规定是,Number办法的参数是对象时,将返回NaN,除非是蕴含单个数值的数组。 Number({a: 1}) // NaNNumber([1, 2, 3]) // NaNNumber([5]) // 5之所以会这样,是因为Number背地的转换规则比较复杂。 第一步,调用对象本身的valueOf办法。如果返回原始类型的值,则间接对该值应用Number函数,不再进行后续步骤。第二步,如果valueOf办法返回的还是对象,则改为调用对象本身的toString办法。如果toString办法返回原始类型的值,则对该值应用Number函数,不再进行后续步骤。第三步,如果toString办法返回的是对象,就报错。 请看上面的例子。 var obj = {x: 1};Number(obj) // NaN// 等同于if (typeof obj.valueOf() === 'object') { Number(obj.toString());} else { Number(obj.valueOf());}下面代码中,Number函数将obj对象转为数值。背地产生了一连串的操作,首先调用obj.valueOf办法, 后果返回对象自身;于是,持续调用obj.toString办法,这时返回字符串[object Object],对这个字符串应用Number函数,失去NaN。 ...

April 7, 2021 · 2 min · jiezi

关于前端基础:JavaScript之原型

[[Prototype]]JavaScript中的对象(函数也是对象)有一个非凡的[[Prototype]]内置属性,所谓的原型链就是由它“链”起来的。 属性查找当援用对象的属性时会触发[[Get]]操作,能够了解为会执行[[Get]](),其逻辑是先查找以后对象是否存在该属性,如果存在就应用它。否则就去递归遍历,查找[[Prototype]]属性所援用的对象中是否存在要查找的属性,如果找到则返回,否则直到[[Prototype]]=null时查找完结,此时返回undefined。 在应用for in遍历对象时原理和查找[[Prototype]]链相似,任何能够通过原型链拜访到并且是enumerable的属性都会被枚举。应用in操作符来查看属性在对象中是否存在时,会查找对象的整条原型链。 属性设置和屏蔽上面以myObject.foo = 'bar'为例来阐明所有可能呈现的状况: 如果myObject本身存在foo属性,不论其[[Prototype]]链下层是否存在,都会产生屏蔽景象,会从新赋值。如果myObject本身不存在,[[Prototype]]链上也不存在,则foo属性会被增加到myObject上。如果myObject本身不存在,[[Prototype]]链上存在,会呈现如下三种状况: 如果[[Prototype]]链上的foo为一般数据拜访属性,并且没有被标记为只读,那就会在myObject上增加foo属性,它是屏蔽属性。如果[[Prototype]]链上的foo被标记为只读,如果运行在严格模式下,会抛出一个谬误。否则,这条赋值语句被疏忽,并不会产生属性屏蔽。如果[[Prototype]]链上的foo是一个setter,那么肯定会调用这个setter。foo不会增加到myObject,也不会从新定义foo这个setter。如果心愿在第二种和第三种下也屏蔽foo,那就不能应用=操作符来赋值,而是应用Object.defineProperty()来向myObject增加foo。 上面来个例子领会一下,同时为原型继承做一个铺垫 var person = {name: 'a'};var fn = { getName: function() { return this.name; }};先想一下person为什么能够调用到Object.prototype中定义的办法?Object被定义为函数,上面会提到,只有是函数都会存在prototype属性,它指向一个对象,被称为原型对象,toString、hasOwnProperty等办法就定义在该原型对象上。var person = {name: 'a'};执行时会创立一个新的对象,并且底层会先将person的[[Prototype]]属性值设置为Object.prototype,相当于执行Object.setPrototypeOf({name: a}, Object.prototype)。这样在整个[[Prototype]]链上就能够找到这些办法了。 再想一下咱们怎么能使person对象能够调用fn中的getName办法呢?应用Object.assign(person, fn)将fn的getName间接增加到person对象中。应用Object.setPrototypeOf(person, fn),会将person中的[[Prototype]]属性由默认的Object改为指向fn对象,而fn中的[[Prototype]]会指向Object.prototype。在执行person.getName()时会进行属性查找,依据下面提到的规定,在其[[Prototype]]链上能够找到getName办法,其中的this利用的隐式绑定。除此之外,还有另外一种实现形式,其原理和第二条一样: var fn = { getName: function() { return this.name; }};// 创立一个新对象person,使其[[Prototype]]指向fnvar person = Object.create(fn);person.name = 'a';person.getName(); // 'a'函数中的prototypeJavaScript中的函数有一种非凡个性:所有函数默认都会领有一个名为prototype的私有并且不可枚举的属性,他会指向一个对象:这个对象通常被称为该函数的原型。该函数同时存在内置属性[[Prototype]],留神这两者的区别! 同时该prototype对象存在一个叫constructor的属性,会持有该函数的援用。 这些个性与上面要说的“构造函数”没有任何关系,也就是说只有是函数就有这些个性。 构造函数JavaScript中把首字母大写的办法称为构造函数,这只是一种约定,同时这也意味着要应用new关键字来调用。 应用new调用函数会执行上面的步骤: 创立一个全新的对象。这个新对象会被执行[[Prototype]]连贯。这个新对象会被绑定到函数调用的this。如果函数没有返回其它对象。那么new表达式中的函数调用会主动返回这个对象。上面是伪代码 function customNew(fn) { var o = {}; var rs = fn.apply(o, [].slice.call(arguments, 1)); Object.setPrototypeOf(o, fn.prototype); return typeof rs === 'undefined' ? o : rs;}function Person(name) { this.name = name;}Person.prototype.getName = function() { return this.name;};var p = customNew(Person, 'JS');p.getName(); // 'JS'console.log(p.constructor === Person); // truePerson的实例能够拜访到getName和constructor都是基于“属性拜访”的原理。 ...

April 7, 2021 · 1 min · jiezi

关于前端基础:JavaScript之this

this是在运行时绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。this的绑定和函数申明的地位没有任何关系,只取决于函数的调用形式。当一个函数被调用时,会创立一个流动记录(有时也称为执行上下文)。这个记录会蕴含函数在哪里被调用(调用栈)。函数的调用形式、传入的参数等信息。this就是这个记录的一个属性,会在函数执行的过程中用到。默认绑定独立函数调用,无奈利用其它规定时的默认规定。如果应用严格模式,则不能将全局对象用于默认绑定,因而this会绑定到undefined。 隐式绑定调用地位是否有上下文对象,或者说是否被某个对象领有或者蕴含。如下所示,调用地位会应用obj上下文援用函数,因而能够说函数被调用时obj对象“领有”或者“蕴含”它。 function foo() { console.log(this.a);}var obj = { a: 2, foo: foo};obj.foo(); // 2这种状况同样实用于foo存在于obj的原型链上,如下所示: function foo() { console.log(this.a);}var fo = { foo: foo};// 创立一个对象obj,使之原型指向fovar obj = Object.create(fo);obj.a = 2;obj.foo(); // 2对象属性援用链中只有上一层或者最初一层在调用地位中起作用,如下 function foo() { console.log(this.a);}var obj2 = { a: 42, foo: foo};var obj1 = { a: 2, obj2: obj2};obj1.obj2.foo(); // 42隐式失落一个最常见的this绑定问题就是被隐式绑定的函数会失落绑定对象,也就是说它会利用默认绑定。function foo() { console.log(this.a);}var obj = { a: 42, foo: foo};var bar = obj.foo;var a = "oops";bar(); // oops尽管bar是obj.foo的一个援用,但实际上,它援用的是foo函数自身,因而此时的bar()其实是一个不带任何润饰的函数调用,因而利用了默认绑定。 ...

April 7, 2021 · 1 min · jiezi