当前位置: 首页 > 后端技术 > Node.js

Nodejs中的一些小技巧

时间:2023-04-03 14:47:38 Node.js

以前因为不注意,用PHP或者Java的方式写nodejs,犯了一些错误。这里有一些小技巧来展示nodejs的不同之处。需要注意的事项。变量提升varvariable='global';控制台日志(变量);函数fn(){console.log(变量);var变量='本地';console.log(变量);}fn();你可能会认为这段代码的执行结果是:globalgloballocal,但实际上结果是globalundefinedlocal。原因是函数作用域导致局部变量在整个函数体内可见,所以执行变成:functionfn(){varvariableconsole.log(variable);变量='本地';控制台日志(变量);}函数内部的console.log根据就近原则读取内部变量,即局部变量覆盖全局变量,那么局部变量在整个函数体中都是可见的,所以相当于升级了变量声明,即变量声明放在函数的开头,但变量初始化还在原来的位置,所以就是上图的顺序。在编写Java时,我们倾向于先声明一个局部变量,然后再使用它,这有助于我们明确它的作用域和生命周期;但是JavaScript没有块级作用域,所以最好把局部变量写在函数的开头,这样可以更清楚地显示它的作用域(整个函数内部)和生命周期,避免误解。需要注意的是,写var和不写var是有区别的:console.log(a);一=1;会报错,如下:console.log(a);变量a=1;结果是undefined,也就是说,没有var的声明不会被提升。函数提升js中创建函数的方式有两种:函数声明和函数字面量。函数提升仅存在于函数声明中:console.log(f1);控制台日志(f2);functionf1(){}varf2=function(){}结果:[Function:f1]undefined表示函数提升导致顺序改变For:functionf1(){}    console.log(f1);控制台日志(f2);varf2=function(){}原型继承的陷阱JavaScript不提供对象继承的语言级特性,而是通过原型实现的。varutil=require('util')functionSuperclass(){this.a='a';}Superclass.prototype.d='d';functionSubclass(){this.b='b';}util.inherits(子类,超类);varsuperC=newSuperclass();varsubC=newSubclass();console.log(superC.a);console.log(subC.a);subC.a='suba';控制台。日志(superC.a);console.log(subC.a);subC.cc='cc';console.log(superC.cc);console.log(subC.cc);console.log(superC.d);console.log(subC.d);结果:aundefinedasubaundefinedccddSuperclass{a:'a'}subC只继承了原型中superC定义的属性d,构造函数内部创建的a属性不被subC继承。同时,原型中定义的属性不会作为对象的属性被console.log输出。修改subC中的属性a不会修改superC的属性a,但是可以得到superC的属性d,设置属性cc不会影响superC。因此set操作不会修改原型链,只有get操作才会实现原型链的存在(继承)。varutil=require('util')functionSuperclass(){this.a='a';}Superclass.prototype.d='d';functionSubclass(){this.b='b';}util.inherits(Subclass,Superclass);varsuperC=newSuperclass();varsubC=newSubclass();for(xinsubC){console.log(x);}结果为bd,也就是说in关键字可以检测self有属性和继承熟悉,这个可以换成!==for(xinsubC){if(subC[x]!==undefined)console.log(x);}结果是一样的,但是在可以区分不同的属性存在和属性存在和未定义两种情况,但是!==无法区分。看下面:for(xinsubC){if(subC.hasOwnProperty(x))console.log(x);}结果是b,说明hasOwnProperty可以检测自己的属性,不包括继承的属性。总结:superC.hasOwnProperty();//自己的属性为truesuperC.propertyIsEnumerable(superC);//可枚举属性为真Object.keys(superC);//所有可枚举的自身属性Object.getOwnPropertyNames(superC);//superC中x的所有自身属性//继承自自身的可枚举属性及其原型链还在范围内看这段代码:for(vari=0;i<5;i++){setTimeout(function(){console.log(i);},100*i)}你可能认为结果是01234但结果是55555原因是settimeout的回调函数在执行的时候,for循环已经执行。i变为5,回调函数最近的原型作用域(这里是全局作用域)上的i为5,自然是5。正确的达到效果的方法是:for(vari=0;i<5;i++){(function(i){setTimeout(function(){console.log(i);},100*i)})(i)}是使用(function(i){})(i);生成一个immediatescope,保证settimeout回调函数执行时,距离最近的prototypescope中的i就是当时循环中的i。说到这里,就不得不说到闭包了:所谓“闭包”是指一个表达式(通常是一个函数)有很多变量和一个绑定这些变量的环境,所以这些变量也是表达式的一部分。通俗点说:闭包的作用就是一个函数执行并返回后,不回收该函数占用的资源,因为函数内部函数(或属性)的执行依赖于函数中的属性。函数outF(){varcount=0;返回函数inF(){count++;控制台日志(计数);}}varinF=outF();inF();//1inF();//2可见outF执行后,其属性count并没有被回收。回到上面的错误循环,for创建了几个闭包,每个闭包共享上下文i。因为for(最有可能)会先运行完,所以当回调函数运行时i已经变成了5。在正确的循环中,闭包也是通过匿名函数创建的。此匿名函数用作外部函数。通过立即调用,settimeout不需要在循环中共享i,而是每个循环都有不同的i。作用域真的可以说是JavaScript中的一个问题。var声明在整个函数内部是可见的,在js1之后是let的出现。变量只属于最近的花括号,看下面代码for(leti=0;i<5;i++){setTimeout(function(){console.log(i);},100*i)}结果是01234不同的是var和let是用来创建变量i的,let使得程序每次进入花括号都会产生一个块级作用域,也就是回调函数中最近的isettimeout的scopechain不再是全局i,而是block-leveli,即每次不同的0,1,2,3,4,而不是全局i,最后5.let产生的效果和上面的直接范围。对象类型很奇怪。在Javascript中没有非常简单的方法来获取对象的类别。instanceof是检查原型链,类似于isPropertypeOf,所以不可能一步得到最准确的对象类型。一般可以使用下面的classof获取最精确的类型vara=newDate();functionclassof(o){if(o===null)return"Null";如果(o===未定义)返回“未定义”;返回Object.prototype。toString.call(o).slice(8,-1);}console.log(classof(a));//Date之所以不直接使用Object.prototype.toString,是因为很多类型重写了这个方法,不能保证它的输出是[objectclass],所以使用Function.call方法。邪恶的分号;nodejs中的分号;是可选的,这个有一定的方便性,但是在我看来比较迷惑,js会在需要的时候帮我们加分号,它有自己添加的规则(当然我们懒得去记)。varaa=3console.log(a)这将被解析为vara;a=3;console.log(a);没有错。但是varequa=function(a,b){if(a===b){返回真;}返回假;}console.log(equa(5,5));//undefined没有按预期执行,因为它被解析成了return;真的;返回的内容自然是未定义的。所以避免混淆最简单的方法就是老老实实地把每一句话都加上去;数组相关a=[];a[1000]=5;//a.length=1001,虽然a只有一个元素a1=[,,,];//[undefined,undefined,undefined]a2=newArray(3);//数组a1中没有元素0;//true,上面说了in可以区分元素值不存在和元素值存在如果是undefined,0ina2;//false高级数组方法filter():“过滤”函数,数组中的每一项运行给定的函数,返回满足过滤条件的数组。vararr=[1,2,3,4,5,6,7,8,9,10];vararr2=arr。filter(function(x,index){返回索引%3===0||x>=8;});控制台日志(arr2);//[1,4,7,8,9,10]every():判断数组中的每一项是否满足条件,只有所有项都满足条件,才会返回true。vararr=[1,2,3,4,5];vararr2=arr.every(function(x){returnx<10;});控制台日志(arr2);//truevararr3=arr.every(function(x){returnx<3;});控制台日志(arr3);//falsesome():判断数组中是否有满足条件的项。只要有满足条件的item,就会返回true。reduce()和reduceRight(),这两个方法会遍历数组的所有项,然后构造一个最终的返回值。reduce()方法从数组的第一项开始,逐项遍历到末尾。而reduceRight()从数组的最后一项开始,向前遍历到第一项。这两种方法都有两个参数:调用每个项目的函数和(可选)合并所基于的初始值。传递给reduce()和reduceRight()的函数接收4个参数:前一个值、当前值、项目的索引和数组对象。此函数返回的任何值都会作为第一个参数自动传递给下一项。第一次迭代发生在数组的第二项上,因此第一个参数是数组的第一项,第二个参数是数组的第二项。下面的代码使用reduce()来实现数组查找。var值=[1,2,3,4,5];var总和=值。reduceRight(function(prev,cur,index,array){returnprev+cur;},0);安慰。日志(总和);//15用this调用函数有4种方式,区别在于调用上下文,即关键字的值(不是变量,不是属性名)this模式下this指向全局对象,在严格模式下它指向未定义。需要注意的是,嵌套函数的this并没有指向外层函数的上下文,也遵循这个规则。方法调用,即调用一个函数作为类的方法,this指向本类的对象构造函数调用,即使用new关键字,this指向新创建的对象本身call,apply调用,this指向给函数绑定的对象参考JavaScript权威指南第六版;JavaScript语言的本质;深入浅出地解释nodejs;http://blog.csdn.net/u0146071...https://developer.mozilla.org...欢迎来到我的主页Mageek`sWonderlandhttp://mageek.cn/archives/32/