javascript秘密花园读书笔记

来源:互联网 发布:宣传单排版软件 编辑:程序博客网 时间:2024/05/01 10:49
  1. toString()方法:当数字字面量使用这个方法时会出现错误,这是因为 JavaScript 解析器的一个错误, 它试图将点操作符解析为浮点数字面值的一部分。
     例:2.toString(); // 出错:SyntaxError
          解决办法:
2..toString(); // 第二个点号可以正常解析
2 .toString(); // 注意点号前面的空格
     (2).toString(); // 2先被计算

2.访问属性:有两种方式来访问对象的属性,点操作符或者中括号操作符
   两种语法是等价的,但是中括号操作符在下面两种情况下依然有效:
          动态设置属性;
          属性名不是一个有效的变量名(属性名中包含空格,或者属性名是 JS 的关键词);

3.删除属性:删除属性的唯一方法是使用 delete 操作符;设置属性为 undefined 或者 null 并不能真正的删除属性, 而仅仅是移除了属性和值的关联

4.原型:
   扩展内置类型的原型:会破坏封装;
   扩展内置类型的唯一理由是为了和新的 JavaScript 保持一致,比如Array.forEach

5.hasOwnProperty 是 JavaScript 中唯一一个处理属性但是查找原型链的函数
  JavaScript 不会保护hasOwnProperty 被非法占用,因此如果一个对象碰巧存在这个属性, 就需要使用外部hasOwnProperty 函数来获取正确的结果
例:
var foo = {
    hasOwnProperty: function() {
        return false;
    },
    bar: 'Here be dragons'
};
 
foo.hasOwnProperty('bar'); // 总是返回 false
 
// 使用其它对象的 hasOwnProperty,并将其上下为设置为foo
{}.hasOwnProperty.call(foo, 'bar'); // true

6.函数声明与函数赋值表达式
   函数声明提升:会在执行前被解析,因此它存在于当前上下文的任意一个地方, 即使在函数定义体的上面被调用也是对的
foo(); // 正常运行,因为foo在代码运行前已经被创建
function foo() {}
函数赋值表达式:赋值语句只在运行时执行      
foo; // 'undefined'
foo(); // 出错:TypeError
var foo = function() {};

命名函数的赋值表达式:将命名函数赋值给一个变量
var foo = function bar() {
    bar(); // 正常运行
}
bar(); // 出错:ReferenceError
注:bar 函数声明外是不可见的,这是因为我们已经把函数赋值给了foo; 然而在 bar 内部依然可见。这是由于 JavaScript 的命名处理所致, 函数名在函数内总是可见的。

7.this的指向
     (1).当在全部范围内使用 this,它将会指向全局对象;
     (2).全局函数调用,指向全局对象;
注:被认为是JavaScript语言另一个错误设计的地方,因为它从来就没有实际的用途
Foo.method = function() {
    function test() {
        // this 将会被设置为全局对象(注:浏览器环境中也就是 window 对象)
    }
    test();
}
  为了在test 中获取对 Foo 对象的引用,我们需要在 method 函数内部创建一个局部变量指向Foo 对象
Foo.method = function() {
    var that = this;
    function test() {
        // 使用 that 来指向 Foo 对象
    }
    test();
}

     (3).调用某个对象的函数,指向当前对象;
     (4).使用New实例化对象时,在函数内部,this 指向新创建的对象;
     (5).当使用 Function.prototype 上的call 或者 apply 方法时,函数内的 this 将会被 显式设置为函数调用的第一个参数;    
     (6).方法的赋值表达式:将一个方法赋值给一个变量
var test = someObject.methodTest;
test();//test 就像一个普通的函数被调用;因此,函数内的this 将不再被指向到 someObject 对象

8.闭包
     (1).模拟私有变量          
function Counter(start) {
    var count = start;
    return {
        increment: function() {
            count++;
        },
 
        get: function() {
            return count;
        }
    }
}
var foo = Counter(4);
foo.increment();
foo.get(); // 5
     (2).循环中的闭包    
for(var i = 0; i < 10; i++) {
    setTimeout(function() {
        console.log(i); 
    }, 1000);
}
         //会输出数字 10 十次;当 console.log 被调用的时候,匿名函数保持对外部变量 i 的引用,此时for循环已经结束, i 的值被修改成了 10
方法一:最好使用 匿名包裹器(注其实就是我们通常说的自执行匿名函数)
for(var i = 0; i < 10; i++) {
    (function(e) {
        setTimeout(function() {
            console.log(e); 
        }, 1000);
    })(i);
}
          //输出数字 09;外部的匿名函数会立即执行,并把 i 作为它的参数,此时函数内 e 变量就拥有了i 的一个拷贝。 当传递给 setTimeout 的匿名函数执行时,它就拥有了对 e 的引用,而这个值是不会被循环改变的
方法二:从匿名包装器中返回一个函数
for(var i = 0; i < 10; i++) {
    setTimeout((function(e) {
        return function() {
            console.log(e);
        }
    })(i), 1000)
}

9.构造函数:通过 new 关键字方式调用的函数
   在构造函数内部 - 也就是被调用的函数内 -this 指向新创建的对象 Object。 这个新创建的对象的 prototype被指向到构造函数的 prototype
(1).如果被调用的函数没有显式的 return 表达式,则隐式的会返回 this 对象 - 也就是新创建的对象
(2).显式的 return 表达式将会影响返回结果,但仅限于返回的是一个对象
function Bar() {
    return new Number(2);
}
new Bar().constructor === Number
(3)Function()和 New Function()
    如果函数返回值为常规意义上的值类型(Number、String、Boolean)时,new 函数将会返回一个该函数的实例对象,
function Student(age){
        this.sex="male";
        this.age=age;
}
test1=Student(18);
test2=new Student(18);
test1就是函数Student()执行的返回值,也就是undefined. 在函数执行过程中,属性被加到全局作用域或者Student方法所属的对象上了。执行之后window.sex=="male"
test2是Student的实例,Student{sex:"male",age:18}
如果函数返回一个引用类型(Object、Array、Function),则 new 函数与直接调用函数产生的结果等同
function Student(age) {
        var obj = new Object();
        obj.sex = "mail";
        obj.age = age;
        return obj;
}
test3=Student(22);//test3是一个Object,不是Man的实例
test4=new Student(22);//test4是Student函数返回的对象,而不是通过new关键字新创建的对象

10.名称解析顺序
当访问函数内的 foo 变量时,JavaScript 会按照下面顺序查找:
  • 当前作用域内是否有 var foo 的定义。
  • 函数形式参数是否有使用 foo 名称的。
  • 函数自身是否叫做 foo
  • 回溯到上一级作用域,然后从 #1 重新开始

11.命名冲突
可以通过 匿名包装器解决
(function() {
    // 函数创建一个命名空间
 
    window.foo = function() {
        // 对外公开的函数,创建了闭包
    };
 
})(); // 立即执行此匿名函数

// 另外两种方式
+function(){}();
(function(){}());

推荐使用匿名包装器(也就是自执行的匿名函数)来创建命名空间。这样不仅可以防止命名冲突, 而且有利于程序的模块化。
另外,使用全局变量被认为是不好的习惯。这样的代码倾向于产生错误和带来高的维护成本。

12.数组遍历:为了更好的性能,推荐使用普通的for 循环并缓存数组的 length 属性。 使用 for in 遍历数组被认为是不好的代码习惯并倾向于产生错误和导致性能问题
注:为 length 设置一个更小的值会截断数组,但是增大length 属性值不会对数组产生影响。
var foo = [1, 2, 3, 4, 5, 6];
foo.length = 3;
foo; // [1, 2, 3]
 
foo.length = 6;
foo; // [1, 2, 3]

13.创建数组  :应该尽量避免使用数组构造函数创建新数组。推荐使用数组的字面语法  
[1, 2, 3]; // 结果: [1, 2, 3]
new Array(1, 2, 3); // 结果: [1, 2, 3]
 
[3]; // 结果: [3]
new Array(3); // 结果: [] 
new Array('3') // 结果: ['3']
 
// 注:因此下面的代码将会使人很迷惑
new Array(3, 4, 5); // 结果: [3, 4, 5] 
new Array(3) // 结果: [],此数组长度为 3
特别注意的是,此时只有 length 属性被设置,真正的数组并没有生成
var arr = new Array(3);
arr[1]; // undefined
1 in arr; // false, 数组还没有生成

14.检测对象类型:为了检测一个对象的类型,强烈推荐使用Object.prototype.toString 方法
Object.prototype.toString.call(obj).slice(8, -1);

Object.prototype.toString.call([])  // "[object Array]"
Object.prototype.toString.call({})  // "[object Object]"
Object.prototype.toString.call(2)   // "[object Number]"

除非为了检测一个变量是否已经定义,我们应尽量避免使用typeof 操作符
instanceof 操作符应该仅仅用来比较来自同一个 JavaScript 上下文的自定义对象。 正如typeof操作符一样,任何其它的用法都应该是避免的。
function Foo() {}
function Bar() {}
Bar.prototype = new Foo();
 
new Bar() instanceof Bar; // true
new Bar() instanceof Foo; // true
 
// 如果仅仅设置 Bar.prototype 为函数 Foo 本身,而不是 Foo 构造函数的一个实例
Bar.prototype = Foo;
     new Bar() instanceof Foo; // false

15.字符串转换为数字的常用方法
+'010' === 10
Number('010') === 10
parseInt('010', 10) === 10  // 用来转换为整数
 
+'010.2' === 10.2
Number('010.2') === 10.2
parseInt('010.2', 10) === 10
注:内置类型(比如 NumberString)的构造函数在被调用时,使用或者不使用 new 的结果完全不同
new Number(10) === 10;     // False, 对象与数字的比较
Number(10) === 10;         // True, 数字与数字的比较
new Number(10) + 0 === 10; // True, 由于隐式的类型转换
通过使用 操作符两次,可以把一个值转换为布尔型:
!!'foo';   // true
!!'';      // false
!!'0';     // true
!!'1';     // true
!!'-1'     // true
!!{};      // true
!!true;    // true




0 0