网上有不少关于JS编写优化建议,这里我根据自己的经验提出一些比较有用的意见。
01按强类型风格写代码
JS是弱类型的,但是写代码的时候不能太随意,写得太随意也体现了编码风格不好。下面分点说明:
(1)定义变量的时候要指明类型,告诉JS解释器这个变量是什么数据类型的,而不要让解释器去猜,例如不好的写法:
var num,
str,
obj;声明了三个变量,但其实没什么用,因为解释器不知道它们是什么类型的,好的写法应该是这样的:
var num = 0,
str = '',
obj = null;定义变量的时候就给他一个默认值,这样不仅方便了解释器,也方便了阅读代码的人,他会在心里有数——知道这些变量可能会当作什么用。
(2)不要随意地改变变量的类型,例如下面代码:
var num = 5;
num = "-" + num;第1行它是一个整型,第2行它变成了一个字符串。因为JS最终都会被解释成汇编的语言,汇编语言变量的类型肯定是要确定的,你把一个整型的改成了字符串,那解释器就得做一些额外的处理。并且这种编码风格是不提倡的,有一个变量第1行是一个整型,第10行变成了一个字符串,第20行又变成了一个object,这样就让阅读代码的人比较困惑,上面明明是一个整数,怎么突然又变成一个字符串了。好的写法应该是再定义一个字符串的变量:
var num = 5;
var sign = "-" + num;(3)函数的返回类型应该是要确定的,例如下面不确定的写法:
function getPrice(count){ if(count < 0) return ""; else return count * 100;
}getPrice这个函数有可能返回一个整数,也有可能返回一个空的字符串。这样写也不太好,虽然它是符合JS语法的,但这种编码风格是不好的。使用你这个函数的人会有点无所适从,不敢直接进行加减乘除,因为如果返回字符串进行运算的话值就是NaN了。函数的返回类型应该是要确定的,如下面是返回整型:
function getPrice(count){ if(count < 0) return -1; else return count * 100;
}然后告诉使用者,如果返回-1就表示不合法。如果类型确定,解释器也不用去做一些额外的工作,可以加快运行速度。
02减少作用域查找
(1)不要让代码暴露在全局作用域下
例如以下运行在全局作用域的代码:
<script>
var map = document.querySelector("#my-map");
map.style.height = "600px";
</script>有时候你需要在页面直接写一个script,要注意在一个script标签里面,代码的上下文都是全局作用域的,由于全局作用域比较复杂,查找比较慢。例如上面的map变量,第二行在使用的时候,需要在全局作用域查找一下这个变量,假设map是在一个循环里面使用,那可能就会有效率问题了。所以应该要把它搞成一个局部的作用域:
<script>
!function(){
var map = document.querySelector("#my-map");
map.style.height = "600px";
}()
</script>上面用了一个function制造一个局部作用域,也可以用ES6的块级作用域。由于map这个变量直接在当前的局部作用域命中了,所以就不用再往上一级的作用域(这里是全局作用域)查找了,而局部作用域的查找是很快的。同时直接在全局作用域定义变量,会污染window对象。
(2)不要滥用闭包
闭包的作用在于可以让子级作用域使用它父级作用域的变量,同时这些变量在不同的闭包是不可见的。这样就导致了在查找某个变量的时候,如果当前作用域找不到,就得往它的父级作用域查找,一级一级地往上直到找到了,或者到了全局作用域还没找到。因此如果闭包嵌套得越深,那么变量查找的时间就越长。如下:
function getResult(count){
count++;
function process(){
var factor = 2; return count * factor - 5;
} return process();
}上面的代码定义了一个process函数,在这个函数里面count变量的查找时间要高于局部的factor变量。其实这里不太适合用闭包,可以直接把count传给process:
function getResult(count){
count++;
function process(count){
var factor = 2; return count * factor - 5;
} return process(count);
}这样count的查找时间就和factor一样,都是在当前作用域直接命中。这个就启示我们如果某个全局变量需要频繁地被使用的时候,可以用一个局部变量缓存一下,如下:
var url = "";if(window.location.protocal === "https:"){
url = "wss://xxx.com" + window.location.pathname + window.location.search;
}频繁地使用了window.location对象,所以可以先把它缓存一下:
var url = "";
var location = window.location;if(location.protocal === "https:"){
url = "wss://xxx.com" + location.pathname + location.search;
}搞成了一个局变局部变量,这样查找就会明显快于全局的查找,代码也可以写少一点。
03避免==的使用
这里你可能会有疑问了,有些人喜欢用==,有些人喜欢用===,大家的风格不一样,你为什么要强制别人用===呢?习惯用==的人,不能仅仅是因为==比===少敲了一次键盘。为什么不提倡用==呢?
(1)如果你确定了变量的类型,那么就没必要使用==了,如下:
if(typeof num != "undefined"){
}
var num = parseInt(value);if(num == 10){
}上面的两个例子都是确定类型的,一个是字符串,一个是整数。就没必要使用==了,直接用===就可以了。
(2)如果类型不确定,那么应该手动做一下类型转换,而不是让别人或者以后的你去猜这里面有类型转换,如下:
var totalPage = "5";if(parseInt(totalPage) === 1){
}(3)使用==在JSLint检查的时候是不通过的:
if(a == b){
}如下JSLint的输出:
Expected ‘===’ and instead saw ‘==’.
if(a == b){(4)并且使用==可能会出现一些奇怪的现象,这些奇怪的现象可能会给代码埋入隐患:
null == undefined //true'' == '0' //false0 == '' //true0 == '0' //true' \t\r\n ' == 0 //true
new String("abc") == "abc" //true
new Boolean(true) == true //true
true == 1 //true上面的比较在用===的时候都是false,这样才是比较合理的。例如第一点null居然会等于undefined,就特别地奇怪,因为null和undefined是两个毫无关系的值,null应该是作为初始化空值使用,而undefined是用于检验某个变量是否未定义。
这和第1点介绍强类型的思想是相通的。
04 合并表达式
如果用1句代码就可以实现5句代码的功能,那往往1句代码的执行效率会比较高,并且可读性可能会更好
(1)用三目运算符取代简单的if-else
如上面的getPrice函数:
function getPrice(count){ if(count < 0) return -1; else return count * 100;
}可以改成:
function getPrice(count){
return count < 0 ? return -1 : count * 100;
}这个比写一个if-else看起来清爽多了。当然,如果你写了if-else,压缩工具也会帮你把它改三目运算符的形式:
function getPrice(e){return 0>e?-1:100*e}(2)连等
连等是利用赋值运算表达式会返回所赋的值,并且执行顺序是从右到左的,如下:
overtime = favhouse = listingDetail = {...}有时候你会看到有人这样写:
var age = 0;if((age = +form.age.value) >= 18){
console.log("你是成年人");
} else {
consoe.log("小朋友,你还有" + (18 - age) + "就成年了");
}也是利用了赋值表达式会返回一个值,在if里面赋值的同时用它的返回值做判断,然后else里面就已经有值了。上面的+号把字符串转成了整数。
(3)自增
利用自增也可以简化代码。如下,每发出一条消息,localMsgId就自增1:
chatService.sendMessage(localMsgId++, msgContent);05减少魔数
例如,在某个文件的第800行,冒出来了一句:
dialogHandler.showQuestionNaire("seller", "sell", 5, true);就会让人很困惑了,上面的四个常量分别代表什么呢,如果我不去查一个那个函数的变量说明就不能够很快地意会到这些常量分别有什么用。这些意义不明的常量就叫“魔数”。
所以最好还是给这些常量取一个名字,特别是在一些比较关键的地方。例如上面的代码可改成:
var naireType = "seller",
dialogType = "sell",
questionsCount = 5,
reloadWindow = true;
naireHandler.showNaire(naireType, dialogType, questionsCount, reloadWindow);这样意义就很明显了。
06 使用ES6简化代码
ES6已经发展很多年了,兼容性也已经很好了。恰当地使用,可以让代码更加地简洁优雅。
(1)使用箭头函数取代小函数
有很多使用小函数的场景,如果写个function,代码起码得写3行,但是用箭头函数一行就搞定了,例如实现数组从大到小排序:
var nums = [4, 8, 1, 9, 0];
nums.sort(function(a, b){ return b - a;
});
//输出[9, 8, 4, 1, 0]如果用箭头函数,排序只要一行就搞定了:
var nums = [4, 8, 1, 9, 0];
nums.sort(a, b => b - a);代码看起来简洁多了,还有setTimeout里面经常会遇到只要执行一行代码就好了,写个function总感觉有点麻烦,用字符串的方式又不太好,所以这种情况用也很方便:
setTimeout(() => console.log("hi"), 3000)箭头函数在C++/Java等其它语言里面叫做Lambda表达式,Ruby比较早就有这种语法形式了,后来C++/Java也实现了这种语法。
当然箭头函数或者Lambda表达式不仅适用于这种一行的,多行代码也可以,不过在一行的时候它的优点才比较明显。
(2)使用ES6的class
虽然ES6的class和使用function的prototype本质上是一样的,都是用的原型。但是用class可以减少代码量,同时让代码看起来更加地高大上,使用function要写这么多:
function Person(name, age){
this.name = name;
this.age = age;
}
Person.prototype.addAge = function(){
this.age++;
};
Person.prototype.setName = function(name){
this.name = name;
};使用class代码看加地简洁易懂:
class Person{
constructor(name, age){
this.name = name;
this.age = age;
}
addAge(){
this.age++;
}
setName(name){
this.name = name;
}
}并且class还可以很方便地实现继承、静态的成员函数,就不需要自己再去通过一些技巧去实现了。
(3)字符串拼接
以前要用+号拼接:
var tpl =
'<div>' +
' <span>1</span>' + '</div>';现在只要用两个反引号“`”就可以了:
var tpl =
` <div>
<span>1</span>
</div>
`;另外反引号还支持占位替换,原本你需要:
var page = 5,
type = encodeURIComponet("#js");
var url = "/list?page=" + page + "&type=" + type;现在只需要:
var url = `/list?page=${page}&type=${type}`;就不用使用+号把字符串拆散了。
(4)块级作用域变量
块级作用域变量也是ES6的一个特色,下面的代码是一个任务队列的模型抽象:
var tasks = [];for(var i = 0; i < 4; i++){
tasks.push(function(){
console.log("i is " + i);
});
}for(var j = 0; j < tasks.length; j++){
tasks[j]();
}但是上面代码的执行输出是4,4,4,4,并且不是想要输出:0,1,2,3,所以每个task就不能取到它的index了,这是因为闭包都是用的同一个i变量,i已经变成4了,所以执行闭包的时候就都是4了。那怎么办呢?可以这样解决:
var tasks = [];for(var i = 0; i < 4; i++){
!function(k){
tasks.push(function(){
console.log("i is " + k);
});
}(i);
}for(var j = 0; j < tasks.length; j++){
tasks[j]();
}把i赋值给了k,由于k它是一个function的一个参数,每次执行函数的时候,肯定会实例化新的k,所以每次的k都是不同的变量,这样就输出就正常了。
但是代码看起来有点别扭,如果用ES6,只要把var改成let就可以了:
var tasks = [];for(let i = 0; i <= 4; i++){
tasks.push(function(){
console.log("i is " + i);
});
}for(var j = 0; j < tasks.length; j++){
tasks[j]();
}只改动了3个字符就达到了目的。因为for循环里面有个大括号,大括号就是一个独立的作用域,let定义的变量在独立的作用域里面它的值也是独立的。当然即使没写大括号for循环执行也是独立的。
除了以上几点,ES6还有其它一些比较好用的功能,如Object的assign,Promise等,也是可以帮助写出简洁高效的代码。
以上列了我自己在实际写代码过程中遇到的一些问题和一些个人认为比较重要的方面,其它的还有变量命名、缩进、注释等,这里就不提及了。写代码的风格也体现了编程的素养,有些人的代码看起来非常地干净利落,而有些人的代码看起来让人比较痛苦。这种编程素质的提升需要有意识地去做一些改进,有些人虽然代码写得很烂,但是他自己并不觉得有什么问题。这就需要多去学下别人的代码,甚至学一下其它语言的书写,两者一比较就能发现差异,或者看下这方面的书,像什么代码大全之类的。
原文:人人网前端团队
js中三种作用域详解(全局,函数,块级)
1.全局变量:声明在函数外部的变量(所有没有var直接赋值的变量都属于全局变量)
2.局部变量:声明在函数内部的变量(所有没有var直接赋值的变量都属于全局变量)
JS中变量申明分显式申明和隐式申明。
在函数中使用var关键字进行显式申明的变量是做为局部变量,而没有用var关键字,使用直接赋值方式声明的是全局变量。
当我们使用访问一个没有声明的变量时,JS会报错。而当我们给一个没有声明的变量赋值时,JS不会报错,相反它会认为我们是要隐式申明一个全局变量,这一点一定要注意。
全局作用域针对于全局变量来说;
全局变量在整个上下文都有效只是在没有赋值之前调用,会输出undefin
函数作用域是针对局部变量来说的,在函数中定义的变量在函数外不能获取
块级作用域
概念“{}”中间的部分都是块级作用域ex:for while if ,js中没有块级作用域,但是可以用闭包实现类似功能。
在C/C++中,由花括号封闭的代码块都有自己的作用域,也就是块级作用域(私有作用域)。而在javascript中则没有块级作用域,首先来看一段代码:
对于有块级作用域的语言来说,for语句中定义并初始化的变量i在循环外是无法访问的,而在javascript中,for语句中定义的变量i在循环结束后,依旧会存在于循环外部的执行环境(作用域)中,在这里i的作用域是全局环境。具体来说就是:使用var关键字声明变量时,这个变量会自动添加到距离最近的可用环境中。对于函数而言,这个最近的环境就是函数的局部环境。如果变量在未经声明的情况下被初始化,则该变量会被自动添加到全局环境。
不过有时候的确很需要块级作用域来解决一些问题,这时候我们就可以使用匿名函数来模仿块级作用域。
匿名函数就是没有名字的函数,有时候也被称为拉姆达(lamda)函数。形式如下:
而用作模仿块级作用域(私有作用域)的匿名函数的语法形式如下:
以上代码的意思是:首先定义并立即调用一个匿名函数。将函数声明包含在圆括号中,表示它实际上是一个函数表达式。而紧随其后的另一对圆括号表示立即调用这个函数。
可能刚开始我们感觉这种语法比较难以理解,我们可以先看下下面这段代码:
上面的代码中,首先以函数表达式的方式定义了一个函数,然后立即调用它。在这里定义函数的方式就是先创建一个匿名函数,然后将其赋值给变量myFunc,而在函数名称后加一对圆括号即表示调用函数。那我们如果直接用匿名函数代表变量myFunc,在匿名函数后面添加一对圆括号不就表示直接调用了吗?然而,如果如下这样做就会报错:
因为在javascript中,function关键字表示一个函数声明的开始,而函数声明后面不能直接跟圆括号。而函数表达式后面可以跟圆括号,来表示函数调用。在函数声明外面加一对圆括号就可以转换成函数表达式,如下:
这样最简单的块级作用域就创建好了。这种技术长在全局作用域中用在函数外部,来限制向全局作用域中添加过多的变量和函数。例如:
以上代码放在全局作用域中,用来确定在1月1日显示一条祝贺新年的信息。其中变量now现在就是匿名函数模仿的块级作用域中的局部变量。
当然,只要我们临时需要一些变量,都可以使用块级作用域(私有作用域)。当匿名函数执行完毕,其作用域链立即销毁,从而可以减少闭包占用资源问题。
javascript 的 "!function" 是什么意思?
这种叫自执行函数表达式
在这种情况下,解析器在解析function关键字的时候,会将相应的代码解析成function表达式,而不是function声明。
#1
叹号后面跟函数!function
和加号后面跟函数+function
都是跟(function(){})();这个函数是一个意思,都是告诉浏览器自动运行这个匿名函数的,因为!+()这些符号的运算符是最高的,所以会先运行它们后面的函数
js 中 var v=v || [] ;这种写法有什么意义?为什么不直接var v='';
这种写法就是如果v为null和undefined的就赋值[]
js 这种写法是什么意思 var a= b || c
+号把字符串转成了整数
箭头函数
模板字符串
传统的JavaScript语言,输出模板通常是这样写的。
上面这种写法相当繁琐不方便,ES6引入了模板字符串解决这个问题。
模板字符串(template string)是增强版的字符串,用
标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。
上面代码中的模板字符串,都是用反引号表示。如果在模板字符串中需要使用反引号,则前面要用反斜杠转义。
如果使用模板字符串表示多行字符串,所有的空格和缩进都会被保留在输出之中。
上面代码中,所有模板字符串的空格和换行,都是被保留的,比如<ul>标签前面会有一个换行。如果你不想要这个换行,可以使用trim方法消除它。
模板字符串中嵌入变量,需要将变量名写在${}之中。
大括号内部可以放入任意的JavaScript表达式,可以进行运算,以及引用对象属性。
模板字符串之中还能调用函数。
如果大括号中的值不是字符串,将按照一般的规则转为字符串。比如,大括号中是一个对象,将默认调用对象的toString方法。
如果模板字符串中的变量没有声明,将报错。
由于模板字符串的大括号内部,就是执行JavaScript代码,因此如果大括号内部是一个字符串,将会原样输出。
模板字符串甚至还能嵌套。
上面代码中,模板字符串的变量之中,又嵌入了另一个模板字符串,使用方法如下。
如果需要引用模板字符串本身,在需要时执行,可以像下面这样写。
阮一峰的es6入门
mdn文档
You-Dont-Know-JS
#8
#个人见解#
当使用的字段名、表名等与MySQL保留字冲突时(如创建名为desc的字段、往desc表中插入记录),如果不加反引号``,无法执行成功。
因此,INSERT `desc` VALUES('aa','bb');可以执行成功。而INSERT desc VALUES('aa','bb');执行失败。
而当你所使用的的字段名、表名不含有保留字时,可以不加反引号``
#8
#10
Html转义字符
二进制是Binary,简写为B
八进制是Octal,简写为O
十进制为Decimal,简写为D
十六进制为Hexadecimal,简写为H
#12
在计算机语言中常用的进制有二进制、八进制、十进制和十六进制,十进制是最主要的表达形式。
对于进制,有两个基本的概念:基数和运算规则。
基数:基数是指一种进制中组成的基本数字,也就是不能再进行拆分的数字。二进制是0和1;八进制是0-7;十进制是0-9;十六进制是0-9+A-F(大小写均可)。也可以这样简单记忆,假设是n进制的话,基数就是【0,n-1】的数字,基数的个数和进制值相同,二进制有两个基数,十进制有十个基数,依次类推。
运算规则:运算规则就是进位或错位规则。例如对于二进制来说,该规则是“满二进一,借一当二”;对于十进制来说,该规则是“满十进一,借一当十”。其他进制也是这样。
#12
js实现二进制与十进制的相互转换
#14
注释:对数字调用 toString(10) 与调用 toString() 相同,它们返回的都是该数字的十进制形式。
ECMAScript 类型转换
#14十进制转二进制
用2辗转相除至结果为1
将余数和最后的1从下向上倒序写
就是结果
例如
302 302/2 = 151 余0
151/2 = 75 余1
75/2 = 37 余1
37/2 = 18 余1
18/2 = 9 余0
9/2 = 4 余1
4/2 = 2 余0
2/2 = 1 余0
故二进制为100101110
二进制转十进制
从最后一位开始算
依次列为第0、1、2...位
第n位的数(0或1)乘以2的n次方
得到的结果相加就是答案
例如:01101011
转十进制:
第0位:1乘2的0次方=1 1乘2的1次方=2 0乘2的2次方=0 1乘2的3次方=8 0乘2的4次方=0 1乘2的5次方=32 1乘2的6次方=64 0乘2的7次方=0 然后:1+2+0 +8+0+32+64+0=107. 二进制01101011=十进制107.
#10
错误😬
#8反引号怎么打
今天我发现我从来没打过这外符号
, 直接按下即可....
刚开始我还一直在找没找到.....🤔
#17
#16
js中函数表达式、函数声明和立即执行函数归纳
函数声明、函数表达式、匿名函数
函数声明:function fnName () {…};使用function关键字声明一个函数,再指定一个函数名,叫函数声明。
函数表达式 var fnName = function () {…};使用function关键字声明一个函数,但未给函数命名,最后将匿名函数赋予一个变量,叫函数表达式,这是最常见的函数表达式语法形式。
匿名函数:function () {}; 使用function关键字声明一个函数,但未给函数命名,所以叫匿名函数,匿名函数属于函数表达式,匿名函数有很多作用,赋予一个变量则创建函数,赋予一个事件则成为事件处理程序或创建闭包等等。
函数声明和函数表达式不同之处在于,一、JavaScript引擎在解析javascript代码时会‘函数声明提升’(Function declaration Hoisting)当前执行环境(作用域)上的函数声明,而函数表达式必须等到Javascirtp引擎执行到它所在行时,才会从上而下一行一行地解析函数表达式,二、函数表达式后面可以加括号立即调用该函数,函数声明不可以,只能以fnName()形式调用 。以下是两者差别的两个例子。
在理解了一些函数基本概念后,回头看看( function(){…} )()和( function (){…} () )这两种立即执行函数的写法,最初我以为是一个括号包裹匿名函数,并后面加个括号立即调用函数,当时不知道为什么要加括号,后来明白,要在函数体后面加括号就能立即调用,则这个函数必须是函数表达式,不能是函数声明。
可以看到输出结果,在function前面加!、+、 -甚至是逗号等到都可以起到函数定义后立即执行的效果,而()、!、+、-、=等运算符,都将函数声明转换成函数表达式,消除了javascript引擎识别函数表达式和函数声明的歧义,告诉javascript引擎这是一个函数表达式,不是函数声明,可以在后面加括号,并立即执行函数的代码。
加括号是最安全的做法,因为!、+、-等运算符还会和函数的返回值进行运算,有时造成不必要的麻烦。
不过这样的写法有什么用呢?
javascript中没用私有作用域的概念,如果在多人开发的项目上,你在全局或局部作用域中声明了一些变量,可能会被其他人不小心用同名的变量给覆盖掉,根据javascript函数作用域链的特性,可以使用这种技术可以模仿一个私有作用域,用匿名函数作为一个“容器”,“容器”内部可以访问外部的变量,而外部环境不能访问“容器”内部的变量,所以( function(){…} )()内部定义的变量不会和外部的变量发生冲突,俗称“匿名包裹器”或“命名空间”。
jQuery使用的就是这种方法,将jquery代码包裹在( function (window,undefined){…jquery代码…} (window)中,在全局作用域中调用JQuery代码时,可以达到保护JQuery内部变量的作用。
jQuery中的on与bind绑定事件区别实例详解
bind与on的区别就在于–事件冒泡,关于jquery中的on与bind绑定事件的区别通过本文给大家实例讲解,需要的朋友参考下吧
on(events,[selector],[data],fn)
events:一个或多个用空格分隔的事件类型和可选的命名空间,如”click”或”keydown.myPlugin” 。
selector:一个选择器字符串用于过滤器的触发事件的选择器元素的后代.
data:当一个事件被触发时要传递event.data给事件处理函数。
fn:该事件被触发时执行的函数。 false 值也可以做一个函数的简写,返回false。
bind(type,[data],fn)
为每个匹配元素的特定事件绑定事件处理函数。
jQuery 3.0中已弃用此方法,请用 on()代替。
参数类型跟前面那个on一样.
bind与on的区别就在于–事件冒泡
demo1:
## 点击相应的li弹出里面内容,这里把on换成bind是一样的没有区别.也就是说on不使用selector属性与bind并无区别
demo2:
demo3
事件委托的好处
万一子元素非常多,给每个子元素都添加一个事件,会影响到性能;
为动态添加的元素也能绑上指定事件;
javascript取网页DOM自定义属性值和设置自定义属性值的通用方法