《JavaScript语言精粹》学习笔记
一.in的用法
for...in
枚举一个对象的所有可枚举属性
检测DOM/BOM属性
if ("onclick" in elem) {
// 元素支持onclick
}
if ("onerror" in window) {
// window支持onerror
}检测js对象的原型属性(结合hasOwnProperty()函数)
if ("attr" in obj && !obj.hasOwnProperty("attr")) {
// obj有原型属性attr
}注意:原型属性会被同名的实例属性屏蔽掉,所以无法检测这样的:
function Super(){
this.attr = 1;
}
function Sub(){
this.attr = 2;
}
Sub.prototype = new Super();
var obj = new Sub();
alert(("attr" in obj && !obj.hasOwnProperty("attr"))); /// false
二.||运算符和&&运算符
||可以用来填充默认值,例如:
var obj = {};
obj.name = ""; // 注意js有一堆假值:+0、-0、0、NaN、null、undefined、""、''、false
var name = obj.name || "ayqy";
alert(name);P.S.个人认为这个东西并不好用,除非能确定原属性值不可能是任何形式的假值,否则默认值会覆盖原值
&&可以用来避免从undefined值读取属性引起TypeError,例如:
var obj = {};
//var attr = obj.obj.attr; // 报错,TypeError,不能从undefined值读取属性
var attr = obj.obj && obj.obj.attr; // 只有obj.obj存在且不是假值时才会取其attrP.S.个人认为和&&差不多,不如直接用if检测,不仅能展现更清晰的逻辑流,还便于添加更多的检测条件
P.S.这两种用法是在书的第一部分介绍的,可能更偏向于展示语言特性,并不是推荐用法
个人建议不要这样用,当然,看到这样的代码要能明白其作用与漏洞
三.减少全局变量污染
用“命名空间”,即空对象,实质是把创建的全局变量减少到1个,比如YUI的Y对象,JQuery的JQuery和$对象。。例如:
var app = {}; //命名空间:app
app.Consts = {
// 子命名空间:常量 URL : {
// 子子命名空间:URL
}
}
app.Modules = {
// 子命名空间:模块
}
app.Data = {
// 子命名空间:数据
}用IIFE(匿名函数立即执行),实质完全不创建全局变量了,0污染
(function(){
// Module1
})();
(function(){
// Module2
})();但缺点很明显,无法实现对象缓存和共享,出了闭包就没了
所以一般做法是结合命名空间和IIFE,整体用命名空间组织起来,在模块内部合适的地方用IIFE
四.4种调用模式
方法调用模式:obj.fun();或者obj[”fun”]();
函数调用模式:fun();此时this指向global对象
构造器调用模式:new Fun();新对象的prototype指向Fun的prototype,this指向这个新对象
apply调用模式:fun.apply(this, arrArgs);
五.关于arguments对象
arguments对象不是数组对象,也不支持所有数组方法,只是一个有点特殊的普通对象,特殊在其length属性(动态更新)
可以做如下测试:
function fun(){
var x = Object.prototype.toString.call(arguments);
alert(x);
var arr = [];
alert(Object.prototype.toString.call(arr));
}; fun();
// IE8:[object Object]和[object Array],Chrome:[object Arguments]和[object Array]
// 不一样不要紧,反正都不是Array
一个小技巧:可以用slice方法把arguments对象转换为数组,例如:
function fun(){
//arguments.sort(); // 报错,不支持sort()函数
var arr = Array.prototype.slice.call(arguments); // 转换
arr.sort(); //不报错,说明转换成功
alert(arr);
}; fun(3, 2); // 2, 3
注意:只有slice有这种奇效,concat就没有,虽然无参的slice和无参的concat用于数组的效果一样(都是复制一份)
六.级联(链式调用)的实现方式
让没有返回值的函数返回this,所以支持链式调用,比如JQuery中的:
$("#adBlock").removeClass("active").hide();
七.构造函数调用方式不当会引起作用域污染
如果不用new操作符,直接调用构造函数,比如Fun();此时this指向global属性,即浏览器环境下的window对象,可能会污染全局作用域
八.把形参列表简化为单一对象
例如:
function fun(height, width, margin, padding){ // 参数太长,顺序记不住
// do something
} /*
* @param {number} arg.height 高度
* @param {number} arg.width 宽度
* @param {number} arg.margin 留白
* @param {number} arg.padding 补白
*/
function fun(arg){ // 不用记参数顺序了
// do something
}
好处如下:
调用时不用再关心参数顺序了,接口变得更加易用
可以直接传个JSON对象进去
缺点:要写一堆注释说明具体需要哪些参数,因为通过一个arg完全看不出来
九.防伪对象(持久性的对象)
防伪对象的属性可以被替换或者删除,但该对象的完整性不会受到损害
也被称为持久性的对象,一个持久性对象就是一个简单功能函数的集合
P.S.防伪对象的概念出现在书的[函数化]部分,目前还不能完全理解函数化想要表达的意思,把自己养肥了再看
十.数组
本质是键值对,也就是对象,所以并没有访问速度上的优势
区别是Array.prototype提供了很多数组操作函数,此外还有特殊的length属性
注意: 1. 数组实际占用的内存空间
var arr = [1];
arr[99] = 100;
此时arr指向的值并没有占用100个元素的空间,因为上面的结果相当于:
var arr = {
"0" : 1,
"99" : 100
};
length属性是可写的
可以用arr.length = n;删掉下标值 >= n的所有元素
常见的length属性用法
省计数器的方式:arr[arr.length] = value;
或者更简单的:arr.push(value);
数组类型检测
因为typeof arr返回”object”,而且instanceof操作符在跨frame时失效,所以检测数组类型不太容易
书上给了一种超麻烦的方式:
function isArray(value){
return value &&
typeof value === "object" &&
typeof value.length === "number" &&
typeof value.splice === "function" &&
!(value.propertyIsEnumerable("length"));
}其实有在作者写书的时候还没出现的简单方法:
可以用Object.prototype.toString.call(value) === ‘[object Array/Function…]’来做类型检查,也可以用来区分原生对象和自定义对象,例如:
[object JSON] // 原生JSON对象
[object Object] // 自定义JSON对象关于类型检测的更多信息请查看黯羽轻扬:JS学习笔记11_高级技巧
十一.正则表达式
js的正则表达式功能并不完整,但基本够用,比如只支持3种模式:g/i/m ~ 全局模式/忽略大小写模式/多行模式
而且支持的特殊元字符(\d、\s之类的)也比较少,但好在支持非捕获型分组和正则环视,还是很不错的
所以在js中使用正则表达式需要做更多的测试,更多关于正则表达式的信息请查看黯羽轻扬:正则表达式学习笔记
还有一个不常用的点:RegExp对象的属性
global:是否开了g模式
ignoreCase:返回是否开了i模式
lastIndex:返回下一次exec匹配的起始位置
multiline:返回是否开了多行模式
source:返回正则表达式串
需要特别注意:用字面量方式创建的RegExp对象可能引用同一个实例,例如:
var regex1 = /abc/g;
var regex2 = /abc/g;
var str = "aaa\r\nabc\r\naaa"; alert([regex1.lastIndex, regex2.lastIndex]); // 0, 0
regex1.test(str);
alert([regex1.lastIndex, regex2.lastIndex]); // 8, 0
alert(regex1 === regex2); // false
老版本浏览器最后两行会输出8, 8和true,据说正则字面量共享实例是ES3的规定,本机测试发现IE8不存在问题,但理论上IE6就存在这个问题(出IE6的时候,ES5还没出)
十二. 原生对象操作函数
P.S.此处只介绍需要特别注意的点,完整的各种操作函数请查看黯羽轻扬:JS学习笔记1_基础与常识
1.数组操作函数
arr1.push(arr2);会把arr2作为单个元素插入,例如:
var arr1 = [1];
var arr2 = [2, 3];
arr1.push(arr2);
alert(arr1[2]); // undefined
alert(arr1[1][1]); // 3arr.shift()比arr.pop()慢很多,因为删掉首元需要更新所有元素的索引,而删掉尾元不需要
arr.unshift(),在数组头部插入元素,IE6返回undefined,本来应该返回新数组的长度
2.对象操作函数
obj.hasOwnProperty(“hasOwnProperty”)返回false
3.正则表达式对象操作函数
regex.exec(str)函数功能最强大,当然,执行速度也最慢
regex.test(str)最简单,最快,注意:不要开g模式,因为纯属浪费(test只返回匹配/不匹配,一次扫描就够了)
4.字符串操作函数
str.replace(regex, fun);是很好用的一个函数,可以用regex匹配目标部分,还可以用fun进一步处理匹配的部分
特别注意:如果regex没有开g模式,那么只替换第一个匹配部分,并不是全串替换
str.replace(regex, replacement);的replacement部分中的$有特殊含义:
$$:表示$,如果替换部分中有$需要用$来转义
$&:表示整个匹配的文本
$n:例如$1, $2, $3...表示捕获到的文本
$`:表示位于匹配部分之前的文本
$’:表示位于匹配部分之后的文本
更多详细示例请查看W3School:JavaScript replace() 方法
str.split(regex);存在一个特例,需要特别注意:
var str = "a & b & c"; // &是分隔符
var arr = str.split(/\s*(&)\s*/); // 含捕获
var arr2 = str.split(/\s*&\s*/); // 不含捕获
var arr3 = str.split(/\s*(?:&)\s*/); // 含非捕获
alert(arr); // [a, &, b, &, c]
alert(arr2); // [a, b, c]
alert(arr3); // [a, b, c]如果不小心用了含捕获型分组的正则表达式,结果可能与预期的不同
十三.糟粕
P.S.这里不打算原样照搬书上的内容,只给出笔者同意的最糟糕的地方
自动插入分号。确实不好,比如典型的ASI错误:
function fun(){
return
{
attr : 1;
}
} alert(fun().attr); // TypeError: Cannot read property 'attr' of undefinedtypeof。不好用,这是设计上的失误,历史原因
paseInt()与八进制。一个很隐蔽的错误:
var year = "2015";
var month = "08";
var day = "09"; alert(parseInt(year)); // IE8:2015 Chrome:2015
alert(parseInt(month)); // IE8:0 Chrome:8
alert(parseInt(day)); // IE8:0 Chrome:9因为0开头的数值被认为是八进制,省略第二个参数的parseInt()会把08/09当作八进制来解析,所以结果是0
处理时间日期太容易引起解析错误了,所以最好不要省略parseInt()的第二个参数
P.S.Chrome中正常了,可能是某个ES版本对parseInt()做了修改,Chrome做了实现。所以糟粕并不是永远存在的,当然,是不是糟粕也要看程序员怎么用。。
Unicode。js对Unicode的支持不好,虽然也是历史原因,但不支持就是不支持
P.S.出js的时候Unicode自己也没想到能有100万个字符,js的字符是16位的,能对应前65536个Unicode字符,而Unicode为了扩容,就把剩下的每个字符都用一对字符来表示,但js会把这样的字符当作两个字符,所以。。
十四.鸡肋
P.S.同上,只列笔者同意的
continue性能低。可以if消除
位运算性能低。(其它的书都没有提到这一点)因为要折腾:double -> int -> 做位运算 -> double
new到底应该用还是不用。用的话可能污染作用域
void运算符。单目运算,返回undefined,所以可以用来实现IIFE,例如:
void function(){
// do something
}();作者建议不要用void,因为没什么人知道它是做什么的
十五.JSON
整数的首位不能是0,道格拉斯(发明JSON的大爷)本人说的,数值可以是整数、实数或者科学计数,但首位不能是0,为了避免八进制歧义
测试代码如下:
var json = '{"number" : 9}';
var obj = JSON.parse(json);
alert(obj.number); // 9 var json = '{"number" : 09}'; // 注意前导0
var obj = JSON.parse(json);
alert(obj.number); // 报错
// Chrome:SyntaxError: Unexpected number
// IE8:语法错误
// FF:SyntaxError: JSON.parse: expected ',' or '}' after property value in object
参考资料:
- 《JavaScript语言精粹》
《JavaScript语言精粹》学习笔记的更多相关文章
- js学习笔记:webpack基础入门(一)
之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...
- PHP-自定义模板-学习笔记
1. 开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2. 整体架构图 ...
- PHP-会员登录与注册例子解析-学习笔记
1.开始 最近开始学习李炎恢老师的<PHP第二季度视频>中的“章节5:使用OOP注册会员”,做一个学习笔记,通过绘制基本页面流程和UML类图,来对加深理解. 2.基本页面流程 3.通过UM ...
- 2014年暑假c#学习笔记目录
2014年暑假c#学习笔记 一.C#编程基础 1. c#编程基础之枚举 2. c#编程基础之函数可变参数 3. c#编程基础之字符串基础 4. c#编程基础之字符串函数 5.c#编程基础之ref.ou ...
- JAVA GUI编程学习笔记目录
2014年暑假JAVA GUI编程学习笔记目录 1.JAVA之GUI编程概述 2.JAVA之GUI编程布局 3.JAVA之GUI编程Frame窗口 4.JAVA之GUI编程事件监听机制 5.JAVA之 ...
- seaJs学习笔记2 – seaJs组建库的使用
原文地址:seaJs学习笔记2 – seaJs组建库的使用 我觉得学习新东西并不是会使用它就够了的,会使用仅仅代表你看懂了,理解了,二不代表你深入了,彻悟了它的精髓. 所以不断的学习将是源源不断. 最 ...
- CSS学习笔记
CSS学习笔记 2016年12月15日整理 CSS基础 Chapter1 在console输入escape("宋体") ENTER 就会出现unicode编码 显示"%u ...
- HTML学习笔记
HTML学习笔记 2016年12月15日整理 Chapter1 URL(scheme://host.domain:port/path/filename) scheme: 定义因特网服务的类型,常见的为 ...
- DirectX Graphics Infrastructure(DXGI):最佳范例 学习笔记
今天要学习的这篇文章写的算是比较早的了,大概在DX11时代就写好了,当时龙书11版看得很潦草,并没有注意这篇文章,现在看12,觉得是跳不过去的一篇文章,地址如下: https://msdn.micro ...
- ucos实时操作系统学习笔记——任务间通信(消息)
ucos另一种任务间通信的机制是消息(mbox),个人感觉是它是queue中只有一个信息的特殊情况,从代码中可以很清楚的看到,因为之前有关于queue的学习笔记,所以一并讲一下mbox.为什么有了qu ...
随机推荐
- CentOS 问题集锦
在CentOS 6更新后,不可避免的会在启动选项中产生多个内核选项,一个内核文件大概占100兆左右(一般100M以下),可以使用以下命令进行删除多余的内核. 1.首先列出系统中正在使用的内核: # u ...
- jquery remove/add css
<input type="submit" class="btn btn-primary" id="submit" value=&quo ...
- 处理返回结果(XML)
var xmlHttp function showUser(str) { xmlHttp=GetXmlHttpObject() if (xmlHttp==null) { alert ("Br ...
- 《高性能JavaScript》笔记
1. UI Thread 和 UI Queue,中文叫 UI 线程和UI 队列,JavaScript 和 UI 更新共享的进程通常被称为浏览器UI进程. 2. 如果用户企图在任务运行时于页面交互,不 ...
- easyUI删除行的操作
columns: [[ { field: 'KeyContent', title: '关键词' }, { field: 'ProductType', title: "用品/配件", ...
- 【C#】 目前的技能点
[C#] 目前的技能点 一. C/S 1. WinForm 2. WPF 二.B/S 1. MVC 2. ASPX 3. WebService 4. js 5. jQuery , jQuery UI ...
- ZOJ 2770火烧连营——差分约束
偶尔做了一下差分约束. 题目大意:给出n个军营,每个军营最多有ci个士兵,且[ai,bi]之间至少有ki个士兵,问最少有多少士兵. ---------------------------------- ...
- mac--mac杂记
zsh路径补全.命令补全,命令参数补全,插件内容补全等等.触发补全只需要按一下或两下tab键,补全项可以使用ctrl+n/p/f/b上下左右切换. plugins=(git textmate ruby ...
- 关于jquery判断对象是否为空
1. jquery对象分为两种,一种是dom对象,dom对象会自带一个length属性,所以这种情况: obj.length == 0 可以判断对象为空 2. jquery也可以自定义对象,如 var ...
- kubernetes Ubuntu部署
规划节点 安装 ubuntu 14.04 LTS 准备password-less SSH登录 建立 ssh-key 证书,切换到root 账户,使用命令 ssh-keygen -t rsa Gener ...