由浅入深JavaScript——JavaScript秘密花园笔记总结

万物皆对象,除了undefined和null

//除了undefined和null以外的所有值都是对象
false.toString(); // 'false'
[1, 2, 3].toString(); // '1,2,3'
function Foo(){}
Foo.bar = 1;
Foo.bar; // 1

数字对象使用时注意避免'.'被解析成浮点

2..toString(); // 第二个点号可以正常解析
2 .toString(); // 注意点号前面的空格
(2).toString(); // 2先被计算

对象的属性

//属性名可以是变量名也可以是字符串字面值
var foo = {name: 'kitten'}
var foo = {'name': 'kitten'}

//取值使可以用成员变量的方法取值也可以通过键名取值
foo.name; // kitten
foo['name']; // kitten

//甚至可以传入一个键名的字符串来获取
var get = 'name';
foo[get]; // kitten

//数字的字面值不能用成员变量的方法来取值,因为变量名不能为纯数字
foo.1234; // SyntaxError
foo['1234']; // works

//在低版本的ES中,属性名与保留字相同时,只能使用字符串字面值的方法进行声明
var foo={'delete':'0'};

//删除属性仅能通过delete关键字
delete foo.attribute;

//如果对象包含一个数组,那么length只能获取到数组的长度
//length作为属性,和name是同级别的,其他属性不会影响到length的值
var foo=[1,2,3];
foo.name='foo';
foo.length; //3

对象的原型

function Foo() {
    this.value = 42;
}
Foo.prototype = {
    method: function() {}
};

function Bar() {}

// 设置Bar的prototype属性为Foo的实例对象
Bar.prototype = new Foo();
Bar.prototype.foo = 'Hello World';

// 修正Bar.prototype.constructor为Bar本身
Bar.prototype.constructor = Bar;

var test = new Bar() // 创建Bar的一个新实例

// 原型链
test [Bar的实例]
    Bar.prototype [Foo的实例] 
        { foo: 'Hello World' }
        Foo.prototype
            {method: ...};
            Object.prototype
                {toString: ... /* etc. */};

上面的例子中,test 对象从 Bar.prototype 和 Foo.prototype 继承下来;因此, 它能访问 Foo 的原型方法 method。同时,它也能够访问那个定义在原型上的 Foo 实例属性 value。
需要注意的是 new Bar() 不会创造出一个新的 Foo 实例,而是 重复使用它原型上的那个实例;因此,所有的 Bar 实例都会共享相同的 value 属性。
使用for in遍历对象的属性时,将遍历原型链上所有的属性
如果只遍历对象本身的属性,则需要使用hasOwnProperty进行过滤

hasOwnProperty

hasOwnProperty只查询对象本身是否含有某个属性而不查询对象的原型链
唯一一个处理属性但是不查找原型链的方法

This

  • 全局范围内this指向全局(浏览器内指window,下同)
  • 一般函数直接调用时,函数内的this指向全局
  • 对象的成员方法调用时,方法内的this指向对象本身
  • 在构造函数内部时,this指向新创建的对象
  • 当Function.prototype显示调用apply或call时,函数内的this被设置成第一个参数
  • 当对象的成员方法被赋值给一个变量后,用变量来调用方法,this指向全局

闭包

arguments

  • arguments是一个对象,但与数组极为相似(不能执行数组的pop,push,slice等方法)

构造函数

  • 函数内this指向新创建的Object,新建对象的prototype指向构造函数的prototype。
  • 构造函数如果没有显式return,则隐式返回this对象。
  • 如果return的是字面值,则新建对象的构造函数还是原构造函数,如果return的是一个对象,那么新建对象的构造函数是return的对象的构造函数。
    function Bar(){
    return new Number(2);
    }
    console.log(new Bar().constructor===Number);//output: true

工厂模式

为了不使用new关键字,构造函数必须显式返回一个值
function Bar(){
var value=1;
return {
method: function(){
return value;
}
}
}
Bar.prototype={
foo:function(){}
}
var bar1=new Bar();
var bar2=Bar();//调用返回值与上面一种相同
console.log(bar1.proto===bar2.proto); //output: true
返回一个带method方法的对象,实际上是创建了一个闭包* *

工厂模式实例
  function Foo() {
      var obj = {};
      obj.value = 'blub';

      var private = 2;
      obj.someMethod = function(value) {
          this.value = value;
      }

      obj.getPrivate = function() {
          return private;
      }
      return obj;
  }

工厂模式好处:

  • 避免通过new来调用函数
  • 充分利用私有变量
    工厂模式弊端:
  • 会占用更多内存,因为新建对象不能共享原型上的方法
  • 为实现继承,需要从另一个对象拷贝所有属性,或把一个对象作为原型

作用域与命名空间

JavaScript仅支持函数作用域
声明变量时不加var会声明一个全局变量
var foo=42;//全局变量
function test(){
foo=21;//全局变量
}
test();
console.log(foo);//output:21

JavaScript中局部变量只能通过两种方式生命,一种是作为函数参数,一种是通过var关键字声明

变量声明提升
  bar();
  var bar = function() {};
  var someValue = 42;

  test();
  function test(data) {
      if (false) {
          goo = 1;
      } else {
          var goo = 2;
      }
      for(var i = 0; i < 100; i++) {
          var e = data[i];
      }
  }

JavaScript会自动把var和function声明提升到当前作用域顶部
注意,仅提升声明,赋值语句不会被提升!!
提升之后等价于如下代码:
// var 表达式被移动到这里
var bar, someValue; // 缺省值是 'undefined'

// 函数声明也会提升
function test(data) {
var goo, i, e; // 没有块级作用域,这些变量被移动到函数顶部
if (false) {
goo = 1;
} else {
goo = 2;
}
for(i = 0; i < 100; i++) {
e = data[i];
}
}

  bar(); // 出错:TypeError,因为 bar 依然是 'undefined'
  someValue = 42; // 赋值语句不会被提升规则(hoisting)影响
  bar = function() {};
  test();
名称解析顺序

当访问函数内的foo变量时,JavaScript会按以下顺序查找:

  1. 当前作用域内是否有var foo定义
  2. 函数形式参数是否有foo名称的
  3. 函数自身是否叫做foo
  4. 回溯到上一级作用域然后执行第1步
命名空间

命名冲突可以通过匿名包装器解决

   (function(){
       //函数创建一个命名空间
       window.foo=function(){
           //对外公开的 函数,创建了闭包
       }
   })();//立即执行

数组

  • for in语句可遍历原型链上的所有属性

  • for in的性能不如经典for语句

  • 建议用变量存储length属性而不是每次都去访问它

  • 如果数组的length被赋值为另外的其他数字,则数组会被截断
    *建议使用[]构造数组而非构造函数,理由如下
    [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
    

类型

JavaScript是弱类型语言

等于操作符==
  • 可以通过==来判断两个值是否相等
  • ==符号会在比较时对两个值进行强制类型转换
    "" == "0" // false
    0 == "" // true
    0 == "0" // true
    false == "false" // false
    false == "0" // true
    false == undefined // false
    false == null // false
    null == undefined // true
    " \t\r\n" == 0 // true
严格等于操作符===
  • ===只能用来判断两个相同类型的值是否相等
  • 推荐显式转换类型再用===比较,而非用==直接比较
    #######比较对象
  • 只有同一个对象实例才会被认为是相等的

typeof操作符

Value Class Type
"foo" String string
new String("foo") String string
1.2 Number object
new Number(1.2) Number number
true Boolean boolean
new Date() Date object
new Error() Error object
[1,2,3] Array object
new Array(1,2,3) Array object
new Function("") Function function
/abc/g RegExp object(function in Nitro/V8)
new RegExp("meow") RegExp object(function in Nitro/V8)
{} Object object
new Object() Object object

为了获取对象的 [[Class]],我们需要使用定义在 Object.prototype 上的方法toString。
测试未定义变量
typeof foo !== 'undefined'

instanceof操作符

用于比较两个操作数的构造函数,只有比较自定义对象时才有意义
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

类型转换

// 下面的比较结果是:true
new Number(10) == 10; // Number.toString() 返回的字符串被再次转换为数字

  10 == '10';           // 字符串被转换为数字
  10 == '+10 ';         // 同上
  10 == '010';          // 同上 
  isNaN(null) == false; // null 被转换为数字 0
                  // 0 当然不是一个 NaN(译者注:否定之否定)

  // 下面的比较结果是:false
  10 == 010;//八进制
  10 == '-10';
内置类型构造函数

内置类型(比如 Number 和 String)的构造函数在被调用时,使用或者不使用 new 的结果完全不同。
new Number(10) === 10; // False, 对象与数字的比较
Number(10) === 10; // True, 数字与数字的比较
new Number(10) + 0 === 10; // True, 由于隐式的类型转换

  • 转换为数字
    +'010' === 10
    Number('010') === 10
    parseInt('010', 10) === 10 // 用来转换为整数

    +'010.2' === 10.2
    Number('010.2') === 10.2
    parseInt('010.2', 10) === 10
    
  • 转换为布尔型
    通过使用 否 操作符两次,可以把一个值转换为布尔型。
    !!'foo'; // true
    !!''; // false
    !!'0'; // true
    !!'1'; // true
    !!'-1' // true
    !!{}; // true
    !!true; // true

原文

JavaScript秘密花园

推荐阅读更多精彩内容

  • 原文:http://dmitrysoshnikov.com/ecmascript/javascript-the-c...
    jaysoul阅读 297评论 0 0
  • 0 写在前面的话 大多数的面向对象编程语言中,比如C++和Java,在使用他们完成任务之前,必须创建类(class...
    自度君阅读 757评论 0 3
  • 工厂模式类似于现实生活中的工厂可以产生大量相似的商品,去做同样的事情,实现同样的效果;这时候需要使用工厂模式。简单...
    舟渔行舟阅读 7,139评论 2 17
  • 原文: https://github.com/ecomfe/spec/blob/master/javascript...
    zock阅读 3,112评论 2 36
  • 01 都说习惯成自然 一个好的习惯影响着我们的生活 工作甚至未来的规划 坏的习惯需改正 好的习惯还需适当的奖励 坚...
    控心_ebed阅读 61评论 0 0
  • 尊敬的用户 非常感谢你选择道辅天下科技 道辅团队专注手游辅助开发 辅助平台经过按键官方指导认证 选用独特插件进行判...
    w_359d阅读 126评论 0 0
  • 很久没画画,临了一个喜欢的博主@大橘子嘿嘿的画
    一不小心学环境阅读 99评论 0 1