书写更漂亮规范的js代码
发布时间:2017-07-13, 10:01:04 分类:HTML | 编辑 off 网址 | 辅助
图集1/4
正文 8661字数 440,878阅读
网上有不少关于JS编写优化建议,这里我根据自己的经验提出一些比较有用的意见。01按强类型风格写代码
JS是弱类型的,但是写代码的时候不能太随意,写得太随意也体现了编码风格不好。下面分点说明:
(1)定义变量的时候要指明类型,告诉JS解释器这个变量是什么数据类型的,而不要让解释器去猜,例如不好的写法:
var num,
str,
obj;
Run code
Cut to clipboard
声明了三个变量,但其实没什么用,因为解释器不知道它们是什么类型的,好的写法应该是这样的:
var num = 0,
str = '',
obj = null;
Run code
Cut to clipboard
定义变量的时候就给他一个默认值,这样不仅方便了解释器,也方便了阅读代码的人,他会在心里有数——知道这些变量可能会当作什么用。
(2)不要随意地改变变量的类型,例如下面代码:
var num = 5;
num = "-" + num;
Run code
Cut to clipboard
第1行它是一个整型,第2行它变成了一个字符串。因为JS最终都会被解释成汇编的语言,汇编语言变量的类型肯定是要确定的,你把一个整型的改成了字符串,那解释器就得做一些额外的处理。并且这种编码风格是不提倡的,有一个变量第1行是一个整型,第10行变成了一个字符串,第20行又变成了一个object,这样就让阅读代码的人比较困惑,上面明明是一个整数,怎么突然又变成一个字符串了。好的写法应该是再定义一个字符串的变量:
var num = 5;
var sign = "-" + num;
Run code
Cut to clipboard
(3)函数的返回类型应该是要确定的,例如下面不确定的写法:
function getPrice(count){ if(count < 0) return ""; else return count * 100;
}
Run code
Cut to clipboard
getPrice这个函数有可能返回一个整数,也有可能返回一个空的字符串。这样写也不太好,虽然它是符合JS语法的,但这种编码风格是不好的。使用你这个函数的人会有点无所适从,不敢直接进行加减乘除,因为如果返回字符串进行运算的话值就是NaN了。函数的返回类型应该是要确定的,如下面是返回整型:
function getPrice(count){ if(count < 0) return -1; else return count * 100;
}
Run code
Cut to clipboard
然后告诉使用者,如果返回-1就表示不合法。如果类型确定,解释器也不用去做一些额外的工作,可以加快运行速度。
02减少作用域查找
(1)不要让代码暴露在全局作用域下
例如以下运行在全局作用域的代码:
<script>
var map = document.querySelector("#my-map");
map.style.height = "600px";
</script>
Run code
Cut to clipboard
有时候你需要在页面直接写一个script,要注意在一个script标签里面,代码的上下文都是全局作用域的,由于全局作用域比较复杂,查找比较慢。例如上面的map变量,第二行在使用的时候,需要在全局作用域查找一下这个变量,假设map是在一个循环里面使用,那可能就会有效率问题了。所以应该要把它搞成一个局部的作用域:
<script>
!function(){
var map = document.querySelector("#my-map");
map.style.height = "600px";
}()
</script>
Run code
Cut to clipboard
上面用了一个function制造一个局部作用域,也可以用ES6的块级作用域。由于map这个变量直接在当前的局部作用域命中了,所以就不用再往上一级的作用域(这里是全局作用域)查找了,而局部作用域的查找是很快的。同时直接在全局作用域定义变量,会污染window对象。
(2)不要滥用闭包
闭包的作用在于可以让子级作用域使用它父级作用域的变量,同时这些变量在不同的闭包是不可见的。这样就导致了在查找某个变量的时候,如果当前作用域找不到,就得往它的父级作用域查找,一级一级地往上直到找到了,或者到了全局作用域还没找到。因此如果闭包嵌套得越深,那么变量查找的时间就越长。如下:
function getResult(count){
count++;
function process(){
var factor = 2; return count * factor - 5;
} return process();
}
Run code
Cut to clipboard
上面的代码定义了一个process函数,在这个函数里面count变量的查找时间要高于局部的factor变量。其实这里不太适合用闭包,可以直接把count传给process:
function getResult(count){
count++;
function process(count){
var factor = 2; return count * factor - 5;
} return process(count);
}
Run code
Cut to clipboard
这样count的查找时间就和factor一样,都是在当前作用域直接命中。这个就启示我们如果某个全局变量需要频繁地被使用的时候,可以用一个局部变量缓存一下,如下:
var url = "";if(window.location.protocal === "https:"){
url = "wss://xxx.com" + window.location.pathname + window.location.search;
}
Run code
Cut to clipboard
频繁地使用了window.location对象,所以可以先把它缓存一下:
var url = "";
var location = window.location;if(location.protocal === "https:"){
url = "wss://xxx.com" + location.pathname + location.search;
}
Run code
Cut to clipboard
搞成了一个
03避免==的使用
这里你可能会有疑问了,有些人喜欢用==,有些人喜欢用===,大家的风格不一样,你为什么要强制别人用===呢?习惯用==的人,不能仅仅是因为==比===少敲了一次键盘。为什么不提倡用==呢?
(1)如果你确定了变量的类型,那么就没必要使用==了,如下:
if(typeof num != "undefined"){
}
var num = parseInt(value);if(num == 10){
}
Run code
Cut to clipboard
上面的两个例子都是确定类型的,一个是字符串,一个是整数。就没必要使用==了,直接用===就可以了。
(2)如果类型不确定,那么应该手动做一下类型转换,而不是让别人或者以后的你去猜这里面有类型转换,如下:
var totalPage = "5";if(parseInt(totalPage) === 1){
}
Run code
Cut to clipboard
(3)使用==在JSLint检查的时候是不通过的:
if(a == b){
}
Run code
Cut to clipboard
如下JSLint的输出:
Expected ‘===’ and instead saw ‘==’.
if(a == b){
Run code
Cut to clipboard
(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
Run code
Cut to clipboard
上面的比较在用===的时候都是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;
}
Run code
Cut to clipboard
可以改成:
function getPrice(count){
return count < 0 ? return -1 : count * 100;
}
Run code
Cut to clipboard
这个比写一个if-else看起来清爽多了。当然,如果你写了if-else,压缩工具也会帮你把它改三目运算符的形式:
function getPrice(e){return 0>e?-1:100*e}
Run code
Cut to clipboard
(2)连等
连等是利用赋值运算表达式会返回所赋的值,并且执行顺序是从右到左的,如下:
overtime = favhouse = listingDetail = {...}
Run code
Cut to clipboard
有时候你会看到有人这样写:
var age = 0;if((age = +form.age.value) >= 18){
console.log("你是成年人");
} else {
consoe.log("小朋友,你还有" + (18 - age) + "就成年了");
}
Run code
Cut to clipboard
也是利用了赋值表达式会返回一个值,在if里面赋值的同时用它的返回值做判断,然后else里面就已经有值了。上面的+号把字符串转成了整数。
(3)自增
利用自增也可以简化代码。如下,每发出一条消息,localMsgId就自增1:
chatService.sendMessage(localMsgId++, msgContent);
Run code
Cut to clipboard
05减少魔数
例如,在某个文件的第800行,冒出来了一句:
dialogHandler.showQuestionNaire("seller", "sell", 5, true);
Run code
Cut to clipboard
就会让人很困惑了,上面的四个常量分别代表什么呢,如果我不去查一个那个函数的变量说明就不能够很快地意会到这些常量分别有什么用。这些意义不明的常量就叫“魔数”。
所以最好还是给这些常量取一个名字,特别是在一些比较关键的地方。例如上面的代码可改成:
var naireType = "seller",
dialogType = "sell",
questionsCount = 5,
reloadWindow = true;
naireHandler.showNaire(naireType, dialogType, questionsCount, reloadWindow);
Run code
Cut to clipboard
这样意义就很明显了。
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]
Run code
Cut to clipboard
如果用箭头函数,排序只要一行就搞定了:
var nums = [4, 8, 1, 9, 0];
nums.sort(a, b => b - a);
Run code
Cut to clipboard
代码看起来简洁多了,还有setTimeout里面经常会遇到只要执行一行代码就好了,写个function总感觉有点麻烦,用字符串的方式又不太好,所以这种情况用箭头函数也很方便:
setTimeout(() => console.log("hi"), 3000)
Run code
Cut to clipboard
箭头函数在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;
};
Run code
Cut to clipboard
使用class代码看加地简洁易懂:
class Person{
constructor(name, age){
this.name = name;
this.age = age;
}
addAge(){
this.age++;
}
setName(name){
this.name = name;
}
}
Run code
Cut to clipboard
并且class还可以很方便地实现继承、静态的成员函数,就不需要自己再去通过一些技巧去实现了。
(3)字符串拼接
以前要用+号拼接:
var tpl =
'<div>' +
' <span>1</span>' + '</div>';
Run code
Cut to clipboard
现在只要用两个反引号“`”就可以了:
var tpl =
` <div>
<span>1</span>
</div>
`;
Run code
Cut to clipboard
另外反引号还支持占位替换,原本你需要:
var page = 5,
type = encodeURIComponet("#js");
var url = "/list?page=" + page + "&type=" + type;
Run code
Cut to clipboard
现在只需要:
var url = `/list?page=${page}&type=${type}`;
Run code
Cut to clipboard
就不用使用+号把字符串拆散了。
(4)块级作用域变量
块级作用域变量也是ES6的一个特色,下面的代码是一个任务队列的模型抽象:
块级作用域变量也是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]();
}
Run code
Cut to clipboard
但是上面代码的执行输出是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]();
}
Run code
Cut to clipboard
把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]();
}
Run code
Cut to clipboard
只改动了3个字符就达到了目的。因为for循环里面有个大括号,大括号就是一个独立的作用域,let定义的变量在独立的作用域里面它的值也是独立的。当然即使没写大括号for循环执行也是独立的。
除了以上几点,ES6还有其它一些比较好用的功能,如Object的assign,Promise等,也是可以帮助写出简洁高效的代码。
以上列了我自己在实际写代码过程中遇到的一些问题和一些个人认为比较重要的方面,其它的还有变量命名、缩进、注释等,这里就不提及了。写代码的风格也体现了编程的素养,有些人的代码看起来非常地干净利落,而有些人的代码看起来让人比较痛苦。这种编程素质的提升需要有意识地去做一些改进,有些人虽然代码写得很烂,但是他自己并不觉得有什么问题。这就需要多去学下别人的代码,甚至学一下其它语言的书写,两者一比较就能发现差异,或者看下这方面的书,像什么代码大全之类的。
原文:人人网前端团队
(支付宝)给作者钱财以资鼓励 (微信)→
有过 5 条评论 »
这种叫自执行函数表达式
在这种情况下,解析器在解析function关键字的时候,会将相应的代码解析成function表达式,而不是function声明。
// 下面2个括弧()都会立即执行 (function () { /* code */ } ()); // 推荐使用这个 (function () { /* code */ })(); // 但是这个也是可以用的 // 由于括弧()和JS的&&,异或,逗号等操作符是在函数表达式和函数声明上消除歧义的 // 所以一旦解析器知道其中一个已经是表达式了,其它的也都默认为表达式了 var i = function () { return 10; } (); true && function () { /* code */ } (); 0, function () { /* code */ } (); // 如果你不在意返回值,或者不怕难以阅读 // 你甚至可以在function前面加一元操作符号 !function () { /* code */ } (); ~function () { /* code */ } (); -function () { /* code */ } (); +function () { /* code */ } ();
<script> var s=function(){ return 1; } alert(s()); alert(s); </script>
<script> var s=function(n){ return n; } alert(s(2)); alert(s); </script>
和加号后面跟函数+function
都是跟(function(){})();这个函数是一个意思,都是告诉浏览器自动运行这个匿名函数的,因为!+()这些符号的运算符是最高的,所以会先运行它们后面的函数
还有比如x=x||{}; (function(){})();
var x=1 || 2 || 3;x值为1或者2或者3 x=2||3;x值为2或者3 var a=1||2; var b=2||3; a&b结果是2
js 中 var v=v || [] ;这种写法有什么意义?为什么不直接var v='';
这种写法就是如果v为null和undefined的就赋值[]
js 这种写法是什么意思 var a= b || c
在js中,这相当于一个赋值语句,如果b的值大于0或为true,那么就把b的值赋给a,否在就把c的值赋给a 布尔值,javascript中以下值会被转换为false false undefined null 0 -0 NaN "" ''
+号把字符串转成了整数
<script> var n='5'; console.log(n.constructor); console.log((+n).constructor); console.log(typeof(n)); console.log(typeof(+n)); console.log(isNaN(n)); //console.log(Number.n); console.log(Number.NaN); </script>
箭头函数
var nums = [4, 8, 1, 9, 0]; nums.sort(a, b => b - a); setTimeout(() => console.log("hi"), 3000);
JavaScript 中反单引号 ` ` function () { return `${this.index * (100 / this.number)}%`; }
模板字符串
传统的JavaScript语言,输出模板通常是这样写的。
$('#result').append( 'There are <b>' + basket.count + '</b> ' + 'items in your basket, ' + '<em>' + basket.onSale + '</em> are on sale!' );
上面这种写法相当繁琐不方便,ES6引入了模板字符串解决这个问题。
$('#result').append(` There are <b>${basket.count}</b> items in your basket, <em>${basket.onSale}</em> are on sale! `);
模板字符串(template string)是增强版的字符串,用
反引号(`)
// 普通字符串 `In JavaScript '\n' is a line-feed.` // 多行字符串 `In JavaScript this is not legal.` console.log(`string text line 1 string text line 2`); // 字符串中嵌入变量 var name = "Bob", time = "today"; `Hello ${name}, how are you ${time}?`
上面代码中的模板字符串,都是用反引号表示。如果在模板字符串中需要使用反引号,则前面要用反斜杠转义。
var greeting = `\`Yo\` World!`;
如果使用模板字符串表示多行字符串,所有的空格和缩进都会被保留在输出之中。
$('#list').html(` <ul> <li>first</li> <li>second</li> </ul> `);
上面代码中,所有模板字符串的空格和换行,都是被保留的,比如<ul>标签前面会有一个换行。如果你不想要这个换行,可以使用trim方法消除它。
$('#list').html(` <ul> <li>first</li> <li>second</li> </ul> `.trim());
模板字符串中嵌入变量,需要将变量名写在${}之中。
function authorize(user, action) { if (!user.hasPrivilege(action)) { throw new Error( // 传统写法为 // 'User ' // + user.name // + ' is not authorized to do ' // + action // + '.' `User ${user.name} is not authorized to do ${action}.`); } }
大括号内部可以放入任意的JavaScript表达式,可以进行运算,以及引用对象属性。
var x = 1; var y = 2; `${x} + ${y} = ${x + y}` // "1 + 2 = 3" `${x} + ${y * 2} = ${x + y * 2}` // "1 + 4 = 5" var obj = {x: 1, y: 2}; `${obj.x + obj.y}` // 3
模板字符串之中还能调用函数。
function fn() { return "Hello World"; } `foo ${fn()} bar` // foo Hello World bar
如果大括号中的值不是字符串,将按照一般的规则转为字符串。比如,大括号中是一个对象,将默认调用对象的toString方法。
如果模板字符串中的变量没有声明,将报错。
// 变量place没有声明 var msg = `Hello, ${place}`; // 报错
由于模板字符串的大括号内部,就是执行JavaScript代码,因此如果大括号内部是一个字符串,将会原样输出。
`Hello ${'World'}` // "Hello World"
模板字符串甚至还能嵌套。
const tmpl = addrs => ` <table> ${addrs.map(addr => ` <tr><td>${addr.first}</td></tr> <tr><td>${addr.last}</td></tr> `).join('')} </table> `;
上面代码中,模板字符串的变量之中,又嵌入了另一个模板字符串,使用方法如下。
const data = [ { first: '<Jane>', last: 'Bond' }, { first: 'Lars', last: '<Croft>' }, ]; console.log(tmpl(data)); // <table> // // <tr><td><Jane></td></tr> // <tr><td>Bond</td></tr> // // <tr><td>Lars</td></tr> // <tr><td><Croft></td></tr> // // </table>
如果需要引用模板字符串本身,在需要时执行,可以像下面这样写。
// 写法一 let str = 'return ' + '`Hello ${name}!`'; let func = new Function('name', str); func('Jack') // "Hello Jack!" // 写法二 let str = '(name) => `Hello ${name}!`'; let func = eval.call(null, str); func('Jack') // "Hello Jack!"
阮一峰的es6入门
mdn文档
You-Dont-Know-JS
#个人见解#
当使用的字段名、表名等与MySQL保留字冲突时(如创建名为desc的字段、往desc表中插入记录),如果不加反引号``,无法执行成功。
因此,INSERT `desc` VALUES('aa','bb');可以执行成功。而INSERT desc VALUES('aa','bb');执行失败。
而当你所使用的的字段名、表名不含有保留字时,可以不加反引号``
<input value="Test" type="button" onclick="alert("OK");" /> <input value="Test1" type="button" onclick="alert('OK1');" /> <input value="Test2" type="button" onclick='alert("OK2");' />
Html转义字符
二进制是Binary,简写为B
八进制是Octal,简写为O
十进制为Decimal,简写为D
十六进制为Hexadecimal,简写为H
在计算机语言中常用的进制有二进制、八进制、十进制和十六进制,十进制是最主要的表达形式。
对于进制,有两个基本的概念:基数和运算规则。
基数:基数是指一种进制中组成的基本数字,也就是不能再进行拆分的数字。二进制是0和1;八进制是0-7;十进制是0-9;十六进制是0-9+A-F(大小写均可)。也可以这样简单记忆,假设是n进制的话,基数就是【0,n-1】的数字,基数的个数和进制值相同,二进制有两个基数,十进制有十个基数,依次类推。
运算规则:运算规则就是进位或错位规则。例如对于二进制来说,该规则是“满二进一,借一当二”;对于十进制来说,该规则是“满十进一,借一当十”。其他进制也是这样。
js实现二进制与十进制的相互转换
<script> var a = 1010; alert(a.toString(2)); //转成二进制 alert(parseInt( "101110100 ",2)) ;//转成十进制 </script>
var iNum = 10; alert(iNum.toString(2)); //输出 "1010" alert(iNum.toString(8)); //输出 "12" alert(iNum.toString(16)); //输出 "A"
ECMAScript 类型转换
用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.
<input value="Test" type="button" onclick="alert("OK");" />
今天我发现我从来没打过这外符号
` 就是键盘的左上方, 1的左边不需要组合键
刚开始我还一直在找没找到.....
浅析Js中的单引号与双引号问题 单引号和双引号其实没啥区别,看你自己习惯了 <input type="button" onclick="alert("1")">-------------------不正确 <input type="button" onclick="alert('1')">-------------------正确 双引号中再用双引号要这样: var str = "abc\"def\"ghi" 用反斜杠来禁止解析双引号。 下面是我摘录的,希望对你有用: 在一个网页中的按钮,写onclick事件的处理代码,不小心写成如下: <input value="Test" type="button" onclick="alert(""OK"");" /> IE提示出错后,再漫不经心地改为: <input value="Test" type="button" onclick="alert(\"OK\");" /> 结果还是出错。 这时,我就想不通了,虽然我知道最直接的解决方法是写成这样: <input value="" type="button" onclick="alert('OK');" /> 但为什么javascript中的转义字符\没有效果了呢? 后来找到一段正常的代码: <input value="Test" type="button" onclick="alert("OK");" /> 这时才理解,原来这时,还是归于HTML的管辖范围,所以转义字符应该使用HTML的,而不是javascript的。两个双引号的做法是vbScript的,\"这种做法则是javascript的,而HTML的,则是用"(转义字符),此外还可以使用:(转义字符)"、'。 下面列出各种表达方法: <html> <body> <input value="外双引号内双引号-错误" type="button" onclick="alert("OK");" /><br /> <input value="外单引号内单引号-错误" type="button" onclick='alert('OK');' /><br /> <input value="两个双引号-错误" type="button" onclick="alert(""OK"");" /><br /> <input value="两个单引号-错误" type="button" onclick="alert(''OK'');" /><br /> <input value="\+双引号-错误" type="button" onclick="alert(\"OK\");" /><br /> <input value="\+单引号-错误" type="button" onclick="alert(\'OK\');" /><br /> <input value="外双引号内单引号-OK" type="button" onclick="alert('OK');" /><br /> <input value="外单引号内双引号-OK" type="button" onclick='alert("OK");' /><br /> <input value="外部不使用引号-OK" type="button" onclick=alert('OK');alert("OK"); /><br /> <input value="HTML转义字符"(& # 3 4 ;)-OK" type="button" onclick="alert("OK");" /><br /> <input value="HTML转义字符'(& # 3 9 ;)-OK" type="button" onclick="alert('OK');" /><br /> <input value="HTML转义字符"(& # x 2 2 ;)-OK" type="button" onclick="alert('OK');" /><br /> <input value="HTML转义字符'(& # x 2 7 ;)-OK" type="button" onclick="alert('OK');" /><br /> <input value="HTML转义字符"(& q u o t ;)-OK" type="button" onclick="alert("OK");" /><br /> <input value="HTML转义字符'(& a p o s ;)-IE错误" type="button" onclick="alert('OK');" /><br /> <input value="其它\\-错误" type="button" onclick="alert(\\"OK\\");" /><br /> <input value="其它\& # 3 4 ;-错误" type="button" onclick="alert(\"OK\");" /><br /> </body> </html>
比如2进制数 101.101 转化为十进制 1 0 1 . 1 0 1 按次序来每个括号代表一个数 (2的2次方)+0+(2的0次方)+(2的-1次方)+0+(2的-2次方) 意思就是小数点 后面 按 -1 -2 -3 的次序来开方 2的-2次方等于2的2次方分之一.一个数的负的X次方等于这个数的X次方分之一.
前端双引号单引号,正则反向引用,js比较jq 1.js,jq,css,html属性必须双,如果同时出现需要嵌套使用,属性的规范是双但是也可以用单测试有效 单引号现象举例: jq中获取元素标签是单引号:$('input').click;弹出也是单引号: alert('aaaa');jq根据id获取元素和设置css都一般用单引号:$('#box').css('color', 'red'); 上述两种情况全部换成双引号也是行的! 2.分号的用法: {}作为函数体一般是不需要分号结束的,()一般需要分号结束,除非作为一个函数的参数就不需要分号; 3.js和jq的比较: jquuery是获取的时候是根据css获取元素一样的用.# 而原生的Js却是document.getElementById('box') 没有那些特殊符号,可以看出jq是为了兼容css的特点,更加接近于面向对象的编程传递参数以字面值对象的 形式进行传递参数,也就是js中的字母表示的内容getElementById是用.#这些符号代替了 4.jq的难点 #box + p { color:green; }当前元素的下一个兄弟节点元素; #box ~ p { color:red; } 当前元素的后面所有兄弟节点元素; 5.正则的反向引用: 正则表达式在函数中用的时候最好是单引号:$reg3='/(\d)\1{2}-(\d)\2{2}-(\d)\3{2}/i';其中\1表示反向引用; preg_match($reg3,$str3,$res);
1.全局变量:声明在函数外部的变量(所有没有var直接赋值的变量都属于全局变量)
2.局部变量:声明在函数内部的变量(所有没有var直接赋值的变量都属于全局变量)
JS中变量申明分显式申明和隐式申明。
var i=100;//显式申明 i=100;//隐式申明
在函数中使用var关键字进行显式申明的变量是做为局部变量,而没有用var关键字,使用直接赋值方式声明的是全局变量。
当我们使用访问一个没有声明的变量时,JS会报错。而当我们给一个没有声明的变量赋值时,JS不会报错,相反它会认为我们是要隐式申明一个全局变量,这一点一定要注意。
全局作用域针对于全局变量来说;
<script> alert(c);//输出undefind // alert(d);报错错 var c=3; function test(){ var a=1; b=2; alert(c)//输出三 } alert(c);//输出3 test(); </script>
全局变量在整个上下文都有效只是在没有赋值之前调用,会输出undefin
函数作用域是针对局部变量来说的,在函数中定义的变量在函数外不能获取
<script>function test(){ alert(a);//声明未赋值输出undefine var a=1; alert(a);//1 } // alert(a);报错,外部获取不到 test(); //alert(a);保存不能输出 </script>
块级作用域
概念“{}”中间的部分都是块级作用域ex:for while if ,js中没有块级作用域,但是可以用闭包实现类似功能。
<script> var i=1; if(i==1){ var b=2; } alert(b);//输出2 for(var j=0;j<=2;j++){ var c=3; } alert(c);//输出3 </script>
function test(){ for(var i = 1 ; i <=5; i++){ //i alert(i); } //alert(i); //6 } */ //test();
对于有块级作用域的语言来说,for语句中定义并初始化的变量i在循环外是无法访问的,而在javascript中,for语句中定义的变量i在循环结束后,依旧会存在于循环外部的执行环境(作用域)中,在这里i的作用域是全局环境。具体来说就是:使用var关键字声明变量时,这个变量会自动添加到距离最近的可用环境中。对于函数而言,这个最近的环境就是函数的局部环境。如果变量在未经声明的情况下被初始化,则该变量会被自动添加到全局环境。
不过有时候的确很需要块级作用域来解决一些问题,这时候我们就可以使用匿名函数来模仿块级作用域。
匿名函数就是没有名字的函数,有时候也被称为拉姆达(lamda)函数。形式如下:
function functionName(arg0,arg1){ //函数体 }
而用作模仿块级作用域(私有作用域)的匿名函数的语法形式如下:
(function(){ //块级作用域 })();
以上代码的意思是:首先定义并立即调用一个匿名函数。将函数声明包含在圆括号中,表示它实际上是一个函数表达式。而紧随其后的另一对圆括号表示立即调用这个函数。
可能刚开始我们感觉这种语法比较难以理解,我们可以先看下下面这段代码:
var myFunc=function(){ alert(‘函数’); }; myFunc();
上面的代码中,首先以函数表达式的方式定义了一个函数,然后立即调用它。在这里定义函数的方式就是先创建一个匿名函数,然后将其赋值给变量myFunc,而在函数名称后加一对圆括号即表示调用函数。那我们如果直接用匿名函数代表变量myFunc,在匿名函数后面添加一对圆括号不就表示直接调用了吗?然而,如果如下这样做就会报错:
function(){ //块级作用域 }();
因为在javascript中,function关键字表示一个函数声明的开始,而函数声明后面不能直接跟圆括号。而函数表达式后面可以跟圆括号,来表示函数调用。在函数声明外面加一对圆括号就可以转换成函数表达式,如下:
(function(){ //块级作用域 })();
这样最简单的块级作用域就创建好了。这种技术长在全局作用域中用在函数外部,来限制向全局作用域中添加过多的变量和函数。例如:
(function(){ var now=new Date(); if(now.getMonth()==0&&now.getDate()==1){ alert(“Happy new year”); } })();
以上代码放在全局作用域中,用来确定在1月1日显示一条祝贺新年的信息。其中变量now现在就是匿名函数模仿的块级作用域中的局部变量。
当然,只要我们临时需要一些变量,都可以使用块级作用域(私有作用域)。当匿名函数执行完毕,其作用域链立即销毁,从而可以减少闭包占用资源问题。
// js : () 表示执行 /* function test(){ (function(){ for(var i = 1 ; i <=5; i++){ //i alert(i); } })(); alert(i); } test(); */ //(function(){alert('我直接执行了!');})();
函数声明、函数表达式、匿名函数
函数声明:function fnName () {…};使用function关键字声明一个函数,再指定一个函数名,叫函数声明。
函数表达式 var fnName = function () {…};使用function关键字声明一个函数,但未给函数命名,最后将匿名函数赋予一个变量,叫函数表达式,这是最常见的函数表达式语法形式。
匿名函数:function () {}; 使用function关键字声明一个函数,但未给函数命名,所以叫匿名函数,匿名函数属于函数表达式,匿名函数有很多作用,赋予一个变量则创建函数,赋予一个事件则成为事件处理程序或创建闭包等等。
函数声明和函数表达式不同之处在于,一、JavaScript引擎在解析javascript代码时会‘函数声明提升’(Function declaration Hoisting)当前执行环境(作用域)上的函数声明,而函数表达式必须等到Javascirtp引擎执行到它所在行时,才会从上而下一行一行地解析函数表达式,二、函数表达式后面可以加括号立即调用该函数,函数声明不可以,只能以fnName()形式调用 。以下是两者差别的两个例子。
fnName(); function fnName(){ ... }//正常,因为‘提升’了函数声明,函数调用可在函数声明之前 fnName(); var fnName=function(){ ... }//报错,变量fnName还未保存对函数的引用,函数调用必须在函数表达式之后 var fnName=function(){ alert('Hello World'); }();//函数表达式后面加括号,当javascript引擎解析到此处时能立即调用函数 function fnName(){ alert('Hello World'); }();//不会报错,但是javascript引擎只解析函数声明,忽略后面的括号,函数声明不会被调用 function(){ console.log('Hello World'); }();//语法错误,虽然匿名函数属于函数表达式,但是未进行赋值操作, //所以javascript引擎将开头的function关键字当做函数声明,报错:要求需要一个函数名
在理解了一些函数基本概念后,回头看看( function(){…} )()和( function (){…} () )这两种立即执行函数的写法,最初我以为是一个括号包裹匿名函数,并后面加个括号立即调用函数,当时不知道为什么要加括号,后来明白,要在函数体后面加括号就能立即调用,则这个函数必须是函数表达式,不能是函数声明。
(function(a){ console.log(a); //firebug输出123,使用()运算符 })(123); (function(a){ console.log(a); //firebug输出1234,使用()运算符 }(1234)); !function(a){ console.log(a); //firebug输出12345,使用!运算符 }(12345); +function(a){ console.log(a); //firebug输出123456,使用+运算符 }(123456); -function(a){ console.log(a); //firebug输出1234567,使用-运算符 }(1234567); var fn=function(a){ console.log(a); //firebug输出12345678,使用=运算符 }(12345678)
可以看到输出结果,在function前面加!、+、 -甚至是逗号等到都可以起到函数定义后立即执行的效果,而()、!、+、-、=等运算符,都将函数声明转换成函数表达式,消除了javascript引擎识别函数表达式和函数声明的歧义,告诉javascript引擎这是一个函数表达式,不是函数声明,可以在后面加括号,并立即执行函数的代码。
加括号是最安全的做法,因为!、+、-等运算符还会和函数的返回值进行运算,有时造成不必要的麻烦。
不过这样的写法有什么用呢?
javascript中没用私有作用域的概念,如果在多人开发的项目上,你在全局或局部作用域中声明了一些变量,可能会被其他人不小心用同名的变量给覆盖掉,根据javascript函数作用域链的特性,可以使用这种技术可以模仿一个私有作用域,用匿名函数作为一个“容器”,“容器”内部可以访问外部的变量,而外部环境不能访问“容器”内部的变量,所以( function(){…} )()内部定义的变量不会和外部的变量发生冲突,俗称“匿名包裹器”或“命名空间”。
jQuery使用的就是这种方法,将jquery代码包裹在( function (window,undefined){…jquery代码…} (window)中,在全局作用域中调用JQuery代码时,可以达到保护JQuery内部变量的作用。
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并无区别
<ul> <li>1</li> <li>2</li> <li>3</li> </ul> <script> $(function () { $('ul li').on('click',function(){ alert($(this).text()) }); }) </script>
demo2:
<script> // 这种情况你会发现点击第四个不具备事件.也就是不具备动态绑定事件能力 $(function () { $('ul li').bind('click',function(){ alert($(this).text()) }); var ok = $('<li>4</li>'); $('ul').last().append(ok); }) </script>
demo3
<script> // 换成on的写法,添加selector属性,就是通过事件冒泡,进行了事件委托,把li的事件委托给其父元素,点击后面加入的li就默认绑定了事件 $(function () { $('ul').on('click','li',function(){ alert($(this).text()) }); var ok = $('<li>4</li>'); $('ul').last().append(ok); }) </script>
事件委托的好处
万一子元素非常多,给每个子元素都添加一个事件,会影响到性能;
为动态添加的元素也能绑上指定事件;
HTML元素,属性已经十分丰富了。但是,在某些场合下,也会显得捉襟见肘,这时候自定义属性就发挥了十分关键的作用。 Html元素的自定义属性,使用起来,十分方便,例如: <input type=”button” value=”Click Me, Baby!” /> 假设我们现在需要限制,这个按钮,只能点击2次,然后就失效了。 通常的实现方式,是可以利用全局变量的形式来记录点击次数,但我们这里用自定义属性来实现这个功能,展示一下自定义属性的优势;我们对上面的button做一下改造: <input type=”button” value=”Click Me, Baby!” clickCount=”0” /> 可以看到,我为这个button 增加了一个自定义属性 clickCount, 并将初始值设为 0;下面我们来写实现功能的js代码: 1. 给 button 增加click事件的处理 <input type=”button” value=”Click Me, Baby!” clickCount=”0” onclick=”customAttributeDemo(this);" /> 2. 我们来写 customAttributeDemo(obj) 这个函数 对于IE来讲,使用自定义属性非常简单,因为IE自动将自定义属性解析到了DOM中,和标准属性没有任何区别,IE下的版本: function customAttributeDemo(obj) { if (obj.clickCount === '0') { obj.clickCount = '1'; } else { obj.disabled = true; } } 上面的代码在 FireFox 下将失效,因为FireFox对自定义属性的使用,限制更高,只能使用 attributes[] 集合来访问,FireFox 下的代码: function customAttributeDemo(obj) { if (obj.attributes['clickCount'].nodeValue === '0') { obj.attributes['clickCount'].nodeValue = '1'; } else { obj.disabled = true; } } 上面的代码,也适用于IE,所以,这个代码,就是具有兼容性的代码了,嘿嘿 感谢小秦网友的交流,他给出了 getAttribute 和 setAttribute 的方法: function customAttributeDemo(obj) { if (obj.getAttribute('clickCount') === '0') obj.setAttribute('clickCount', '1'); else obj.disabled = true; }