一.in的用法

  1. for...in

    枚举一个对象的所有可枚举属性

  2. 检测DOM/BOM属性

    if ("onclick" in elem) {
    // 元素支持onclick
    }
    if ("onerror" in window) {
    // window支持onerror
    }
  3. 检测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存在且不是假值时才会取其attr

    P.S.个人认为和&&差不多,不如直接用if检测,不仅能展现更清晰的逻辑流,还便于添加更多的检测条件

P.S.这两种用法是在书的第一部分介绍的,可能更偏向于展示语言特性,并不是推荐用法

个人建议不要这样用,当然,看到这样的代码要能明白其作用与漏洞

三.减少全局变量污染

  1. 用“命名空间”,即空对象,实质是把创建的全局变量减少到1个,比如YUI的Y对象,JQuery的JQuery和$对象。。例如:

    var app = {};   //命名空间:app
    app.Consts = {
    // 子命名空间:常量 URL : {
    // 子子命名空间:URL
    }
    }
    app.Modules = {
    // 子命名空间:模块
    }
    app.Data = {
    // 子命名空间:数据
    }
  2. 用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
}

好处如下:

  1. 调用时不用再关心参数顺序了,接口变得更加易用

  2. 可以直接传个JSON对象进去

缺点:要写一堆注释说明具体需要哪些参数,因为通过一个arg完全看不出来

九.防伪对象(持久性的对象)

防伪对象的属性可以被替换或者删除,但该对象的完整性不会受到损害

也被称为持久性的对象,一个持久性对象就是一个简单功能函数的集合

P.S.防伪对象的概念出现在书的[函数化]部分,目前还不能完全理解函数化想要表达的意思,把自己养肥了再看

十.数组

本质是键值对,也就是对象,所以并没有访问速度上的优势

区别是Array.prototype提供了很多数组操作函数,此外还有特殊的length属性

注意: 1. 数组实际占用的内存空间

var arr = [1];
arr[99] = 100;

此时arr指向的值并没有占用100个元素的空间,因为上面的结果相当于:

var arr = {
"0" : 1,
"99" : 100
};
  1. length属性是可写的

    可以用arr.length = n;删掉下标值 >= n的所有元素

  2. 常见的length属性用法

    省计数器的方式:arr[arr.length] = value;

    或者更简单的:arr.push(value);

  3. 数组类型检测

    因为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.数组操作函数

  1. arr1.push(arr2);会把arr2作为单个元素插入,例如:

    var arr1 = [1];
    var arr2 = [2, 3];
    arr1.push(arr2);
    alert(arr1[2]); // undefined
    alert(arr1[1][1]); // 3
  2. arr.shift()比arr.pop()慢很多,因为删掉首元需要更新所有元素的索引,而删掉尾元不需要

  3. 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 undefined
  • typeof。不好用,这是设计上的失误,历史原因

  • 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语言精粹》学习笔记的更多相关文章

  1. JavaScript语言精粹学习笔记

    0.JavaScript的简单数据类型包括数字.字符创.布尔值(true/false).null和undefined值,其它值都是对象. 1.JavaScript只有一个数字类型,它在内部被表示为64 ...

  2. JavaScript 语言精粹读书笔记

    最近在看 赵泽欣 / 鄢学鹍 翻译的 蝴蝶书, 把一些读后感言记录在这里. 主要是把作者的建议跟 ES5/ES5.1/ES6 新添加的功能进行了对比 涉及到的一些定义 IIFE: Immediatel ...

  3. <JavaScript语言精粹>-读书笔记(一)

    用object.hasOwnProperty(variable)来确定这个属性名是否为该对象成员,还是来自于原型链. for(my in obj){ if(obj.hasOwnProperty(my) ...

  4. JavaScript语言精粹-读书笔记

    前言:很久之前读过一遍该书,近日得闲,重拾该书,详细研究一方,欢迎讨论指正. 目录: 1.精华 2.语法 3.对象 4.函数 5.继承 6.数组 7.正则表达式 8.方法 9.代码风格 10.优美的特 ...

  5. 学习javascript语言精粹的笔记

    1.枚举: 用for in 语句来遍历一个对象中所有的属性名,该枚举过程将会列出所有的属性也包括涵数和方法,如果我们想过滤掉那些不想要的值,最为常用的过滤器为hasOwnProperty方法,以及使用 ...

  6. JavaScript语言精粹读书笔记 - JavaScript函数

    JavaScript是披着C族语言外衣的LISP,除了词法上与C族语言相似以外,其他几乎没有相似之处. JavaScript 函数: 函数包含一组语句,他们是JavaScript的基础模块单元,用于代 ...

  7. 【Javascript语言精粹】笔记摘要

    现在大部分编译语言中都流行要求强类型.其原理在于强类型允许编译器在编译时检测错误.我们能越早检测和修复错误,付出的代价越小.Javascript是一门弱类型的语言,所以Javascript编译器不能检 ...

  8. <JavaScript语言精粹>--<读书笔记三>之replace()与正则

    今天有人问我repalce(),他那个题目很有意思.我也不会做,于是我就去查,结果发现就是最基础的知识的延伸. 所以啊最基础的知识才是很重要的,千万不能忽略,抓起JS就写代码完全不知到所以然,只知道写 ...

  9. 《JavaScript语言精粹》笔记

    0.JavaScript的简单数据类型包括数字.字符创.布尔值(true/false).null和undefined值,其它值都是对象. 1.JavaScript只有一个数字类型,它在内部被表示为64 ...

  10. JavaScript语言精粹读书笔记- JavaScript对象

    JavaScript 对象 除了数字.字符串.布尔值.null.undefined(都不可变)这5种简单类型,其他都是对象. JavaScript中的对象是可变的键控集合(keyed collecti ...

随机推荐

  1. jsp

    -----------------

  2. PhpStorm提高效率的使用方法及设置

    快捷键: CTRL + D  复制当前行到下一行 或 复制选中内容到选中内容之后 CTRL + Y  删除当前行或选中内容所涉及的行 CTRL + R  替换 CTRL + F  查找 ALT + 上 ...

  3. [Redux] Filtering Redux State with React Router Params

    We will learn how adding React Router shifts the balance of responsibilities, and how the components ...

  4. Java在已存在的pdf文件中生成文字和图片--基础

    自我总结,有什么不足之处请告知,感激不尽!下一次总结pdf模板映射生成报表(应对多变的pdf报表需求,数据提供和报表生成解耦). 目的:在给定的pdf模板上生成报表,就需要知道最基本的操作:文字添加, ...

  5. Centos 部署.net Core

    1.安装net core框架 sudo rpm --import https://packages.microsoft.com/keys/microsoft.asc sudo sh -c 'echo ...

  6. jenkins问题整理

    --------------------------------这是一个模板------------------------------------- 问题1:jenkins服务器上传jar包到指定服 ...

  7. AD9361寄存器配置顺序,循环模式,自收自发

    :] cmd_data; :] index; begin case(index) 'h000,8'h00};//set spi -- 'h3df,8'h01};//set init -- 'h037, ...

  8. oracle 11g rac asm磁盘组增加硬盘

    要增加磁盘的磁盘组为:DATA 要增加的磁盘为: /dev/sde1 在第一个节点上:[root@rac1 ~]# fdisk /dev/sdeDevice contains neither a va ...

  9. [转]vi 常用命令行

    From : http://www.cnblogs.com/sunormoon/archive/2012/02/10/2345326.html vi 常用命令行 1.vi 模式  a) 一般模式: v ...

  10. 汉诺塔(Hanoi)——小小算法

    传送门: 袁咩咩的小小博客 汉诺(Hanoi)塔源于古印度,是非常著名的智力趣题,大意如下: 勃拉玛是古印度的一个开天辟地的神,其在一个庙宇中留下了三根金刚石的棒,第一 根上面套着64个大小不一的圆形 ...