当前位置: 首页 > Web前端 > JavaScript

阅读《你不知道的javascript》(中)部分记录-第一部分

时间:2023-03-27 00:56:28 JavaScript

你不知道的JavaScript类型和语法第一部分1.键入a。内置类型JavaScript中的变量没有类型,只有值有类型,变量可以持有任何类型的值。JS中有七种内置类型:nullundefinedbooleannumberstringobjectsymbol可以使用typeof来判断类型,比较特殊的有:typeofnull//==>"object"typeoffunction(){}//==>"function"属于Thesubtypeofobjecttypeof[]//==>"object"属于object的子类型b.typeof未声明。看下面的代码:leta;console.log(a类型);//undefinedconsole.log(typeofb);//undefined虽然b是一个未声明的变量,但是没有报错。这里涉及到typeof这种特殊的安全机制,在判断一个可能未声明的变量是否有值时非常有用://ReferenceErrorif(DEBUG){console.log("debuggingisstarting");}//这是安全的如果(typeofDEBUG!=="undefined"){console.log("debuggingisstarting");}2.值a。Arrayjs数组是一个特殊的js对象。使用删除操作符从数组中删除一个单元后,数组的长度不会改变。直接给数组添加数值索引时,当数组索引的大小大于长度时,数组的长度会变成添加索引的个数加一,添加字符索引不会影响lengthvararr=[];arr[99]='你好';arr.name='arr';console.log(arr.length);//100console.log(arr)//(100)[empty?99,'hello']b.常见的类数组:arguments参数对象,DOM查询操作返回的列表,将类数组转换为数组,可以使用一些数组方法:functionfoo(){vararr=Array.prototype.slice.call(arguments);arr.push('砰');console.log(arr);}foo('jfks','fsssdd')c.StringsJS中的字符串是不可变的,而数组是可变的,字符串类数组,使用字符字符串借用数组的方法可以达到更好的效果:console.log(Array.prototype.map.call('jiang',x=>x.charCodeAt(0)));//[106,105,97,110,103]console.log(Array.prototype.join.call('jiang','-'));//j-i-a-n-gd。js中数字比较小数时,可以看到:0.1+0.2==0.3是false,这是因为JS遵循IEEE754规范,如何判断0.1+0.2==0.3,需要设置一个误差范围(也叫“machineprecision",mechineepsilon)来比较,从ES6开始,这个值定义在Number.EPSILON中,然后可以判断两位小数的大小是否在误差范围内:console.log(Number.EPSILON);//ES6之前的版本写polyfillif(!Number.EPSILON)Number.EPSILON=Math.pow(2,-52);函数numbersCloseEnoughToEqual(n1,n2){返回Math.abs(n1-n2)undefinedf。不是数字的数字NaN表示“不是数字”(不是数字),但将其读作“无效数字”可能更准确。对NaN的判断:vara=2/"jf";console.log(typeofa);console.log(window.isNaN(a));//trueconsole.log(window.isNaN('jiang'));//true//window.isNaN(..);defective//ES6之前Number.isNaN()的polyfill如下if(typeofNumber.isNaN!=='function'){Number.isNaN=function(value){//returntypeofvalue==='number'&&window.isNaN(value);返回值!==值;};}3.本机函数常用原生函数内置原生函数:String(),Number(),Boolean(),Array(),Object(),Function(),RegExp(),Date(),Error(),Symbol()b.内部属性[[class]]所有typeof返回值都是“object”的对象(比如数组)都包含一个内部属性[[class]],我们可以把它看成是对象类型的进一步分类。该属性无法直接获取。一般通过Object.prototype.toString(..)查看:Object.prototype.toString.call([]);//"[对象数组]"Object.prototype.toString.call({});//"[objectObject]"Object.prototype.toString.call(function(){});//"[对象函数]"Object.prototype.toString.call(null);//"[objectNull]"Object.prototype.toString.call(undefi内德);//"[objectUndefined]"如果想获取封装对象中的基本类型值,可以使用valueOf()函数:newString('abc').valueOf()==>'abc'c。NativefunctionArray(..)当Array构造函数只带一个数组参数时,这个参数会作为数组的预设长度,使用newArray创建一个空数组:newArray(6)//[empty?6]console.log(newArray(6).fill(undefined));//使用apply方法生成一个新的数组并填充undefinedconsole.log(Array.apply(null,{length:6}));//apply方法的第一部分第二个参数是一个类数组或者数组对象//apply类似for循环的顺序获取这个类数组的每个元素,并将它们作为参数传递给函数arr[0],arr[1],arr[2]//但此类数组只有length属性,不包含其他属性,arr[0]=undefined,//所以Array.apply(null,{length:6})等价于newArray(undefined,undefined,undefined,undefined,undefined,undefined)4.强制类型转换a.抽象值操作I.ToString抽象操作ToString,她负责处理非字符串到字符串的强制类型转换对于基本类型,null转换为“null”,undefined转换为“undefined”,true转换为“true””。对于普通对象,toString()(Object.prototype.toString())返回内部属性[[objctObject]],除非你自己定义。使用toString:console.log({}.toString());//[object对象]console.log([2,3,4].toString());//2,3,4数组重写toString方法console.log(newDate().toString());//WedDec31196916:00:00GMT+0800(ChinaStandardTime)date覆盖toString方法JSON字符串格式化工具函数JSON.stringify(..)ToString也用于将对象序列化为字符串。JSON字符串格式化与toString()具有基本相同的效果。所有安装的JSON值都可以使用JSON.stringify(..)进行字符串化。不安全的JSON值包括:undefined、function、symbol、包含不符合JSON结构标准的循环引用的对象。JSON.stringify(..)会自动忽略对象中的undefined、function、symbol,返回数组中的null(保证unit位置不变)。console.log(JSON.stringify(undefined));//undefinedconsole.log(JSON.stringify(function(){}));//undefinedconsole.log(JSON.stringify(Symbol('jiang')));//undefinedconsole.log(JSON.stringify([1,undefined,function(){},4]));//[1,null,null,4]console.log(JSON.stringify({name:'jiang',hello:function(){console.log('hello');}}));//{"name":"jiang"}忽略该函数如果对象中定义了toJSON()方法,JSON字符串化后首先调用该方法,再使用其返回值进行序列化。定义一个toJSON()方法来返回一个安全的JSON值。varo={};vara={b:42,c:o,d:function(){}}//在a中创建循环引用o.e=a;//循环引用会在这里产生错误//JSON.stringify(a);//自定义JSON序列化a.toJSON=function(){return{b:this.b,}}console.log(JSON.stringify(a));//{"b":42}JSON.stringify(..)第二个参数可以传入一个数组,数组的元素代表需要JSON字符串化的属性,也可以传入一个函数,会自动生成对象调用一次,然后对对象的每个属性调用一次,每次调用都会传入key和value;如果省略了某个键,它将返回undefined,否则将返回指定的值。JSON.stringigy(..)的第三个参数是序列化后的缩进字符数vara={name:"???",age:20,sex:1}console.log(JSON.stringify(a,(k,v)=>{if(k!=="sex")returnv;},2))II.toNumberwherespecialconversiontrue==>1,false==>0,undefined==>NaN,null==>0;对象(包括数组)会先转换为对应的基本类型值,如果是非数字的基本类型值,则按照上面的特殊转换处理。将值转换为银的原始操作ToPrimitive将首先检查(通过内部操作DefaultValue)该值是否具有valueOf()方法。如果存在并且返回原始类型的值,则将其用于转换。如果不存在,则使用toString的返回值(如果存在)进行转换。如果valueOf()和toString()都没有返回基本类型的值,则会引发TypeError。vara={valueOf:函数(){返回1;}}console.log(1+a);//2varb={toString:function(){return'99';}}console.log(1+Number(b));//100varc=[1,2,3];c.toString=function(){returnthis.join('');}console.log(Number(c));//123III。ToBoolean有以下false值:undefined,null,false,+0,-0,NaN,"",document.all(false值对象,用于判断是否为IE)有以上false值,一般在JS中都有是真实的价值。b.位运算符的神奇作用首先要清楚。在JS中,整数和小数都是以64位浮点数的形式存储的。按位运算符只能对32位整数进行运算,运算符会强制使用32位格式,这是通过抽象运算ToInt32实现的。ToInt32首先进行ToNumber强制类型转换。例如“123”会先转换为123,然后执行ToInt32。下面介绍两种常用的位运算符,位运算符号使用参考:http://c.biancheng.net/view/5...//|按位或运算符,直接用于取整32.32先执行ToInt32(),再执行|operator(and0oroperationgetitself),打印时执行ToString()console.log(32.32|0);//32//~位非运算符,直接用来取整32.32先执行ToInt32(),再执行两次~运算符(get本身),打印console.log(~~32.32)时执行ToString();//-32//~x的值相当于-(x+1)当x=-1时相当于0,当和indexOf(..)一起使用时为falsenotfoundvarstr='helloworld';console.log(!!~str.indexOf('q'));//4c。显示解析出来的数字使用字符串parseInt函数可以让字符串中包含非数字字符,从左到右解析,如如果遇到非数字字符,它将停止。parseInt的第一个参数应该是一个字符串。届时其他类型的值会先转为字符串类型,然后再解析字符串:parseInt(1/0,19)//18(1/0==>InfinityIis18inhexadecimal)parseInt(false,16)//250(fa==>false,前两位为十六进制有效值)parseInt(parseInt,16)//15(f==>function..==>parseInt)d.隐式强制转换一、字符串和数字之间的隐式转换a+""和String的区别(a),根据ToPrimitive抽象操作规则,a+""会先调用a上的valueOf()方法,当返回value不是基本类型,则调用toString()方法,再通过ToString抽象操作将返回值转换为字符串,String(a)直接调用ToString()。当a是对象而不是数字时,两者有区别:vara={valueOf:function(){return2;},toString:function(){返回42;}}a+""//2String(a)//42II.||&&逻辑运算符||(or)和&&(and),但是在JS中,称它为“操作数选择运算符”更为合适,因为在JS中,这两个运算符的返回值不一定是布尔值,而是其中的一个操作数。||运算符,从左到右,返回布尔值中第一个可以转换为true的操作数,全部为false时返回最后一个数:console.log(0||false||''||88||true)//88console.log(0||false||''||undefined||null)//nulla=a||'a'//当a为设定值或false时,a='a',当a为真时,发生短路&&运算符,从左到右,返回第一个可以转为false的操作数布尔值,当全部为真时返回最后一个操作数:console.log(true&&[]&&document.all&&{})//document.all(falsevalueobject)console.log({}&&[]&&true)//truea&&foo()//当a为falsy时,发生短路,foo不会执行III.符号类型的强制转换符号转换时,符号不能隐式转换为字符串,但显示可以强制转换为字符串://console.log(Symbol('hello')+'')//errorconsole.log(String(Symbol('hello')))//Symbol(hello)符号类型不能转换为数字(显示和隐式都会报错),转换为布尔值时,始终为true。三.松散相等是严格相等“==”和“===”的区别,“==”在比较相等时允许强制类型转换,而“===”则不允许。非常规情况:NaN不等于NaN,+0等于-0。不同类型比较(==):字符串和数字比较,先把字符串转成数字再比较非布尔类型和布尔类型,先把布尔类型转成数字(1,0)再比较undefined==null,他们也等于自生,其他值不等于他们。对象与非对象比较,在ToPromitive抽象操作后先执行对象,然后做一个比较练习(提示:使用对象封装,将对应的封装类型封装成原始值):console.log(42==“42”);//true(ToNumber("42")==>42)console.log(42==true);//false(ToBoolean(true)==>1)console.log(undefined==null)//true(没有转换,直接等于)console.log(null==Object(null))//和Object()console.log(undefined==Object(undefined))//和Object()一样console.log(NaN==Object(NaN))//和newNumber(NaN)一样//null和undefined不能封装因为没有对应的封装对象,newObject(null)和newObject(undefined)都返回一个常规对象,不等于原来的值//NaN可以封装为数字封装对象,但是拆包后,NaN==NaN为假5。语法运算符优先级,运算符在表达式中的优先级最低,&&比||优先,||比?:运算符具有更高的优先级,并且?:是右结合运算符。练习:vara=42;varb='foo';varc=false;vard=a&&b||C?c||乙?a:c&&b:a;//vard=((a&&b)||c)?((c||b)?a:(c&&b)):a;//'foo'?('foo'?42:false):42;//42b.自动分号插入(AutomaticSemicolonInsetion,ASI)如果JavaScript解析器发现代码行可能因为缺少分号而导致错误,则会自动补入分号,以及代码行末尾和换行符之间的空格。只有在除了和注释之外没有其他内容的情况下,才会这样做。代码语句块末尾不需要加分号,都是for循环,while循环语句块不需要加;但是需要加入do..while循环;,而且我们在写代码的时候,应该尽量减少对ASI的依赖。