• typeof的类型: number string boolean undefined object function。使用typeof操作符检测函数时,该操作符会返回“function”。其他的new对象返回”object”。
  • Javascript还提供了一个instanceof运算符,验证构造函数与实例对象之间的关系。
  • 为了配合prototype属性,Javascript定义了一些辅助方法,帮助我们使用它:
    • isPrototypeOf():这个方法用来判断,某个proptotype对象和某个实例之间的关系。
    • hasOwnProperty():用来判断某一个属性到底是本地属性,还是继承自prototype对象的属性。
    • in运算符:in运算符可以用来判断,某个实例是否含有某个属性,不管是不是本地属性。
  • 封装与继承的概念与在Javascript实现。
  • 作用域由作用域链构成,作用域实际上就是一些内存区域。
  • 闭包的优点是可以避免全局变量的污染,缺点是闭包会常驻内存。
  • 在Jquery插件内部,this关键字指的是jQuery对象的实例。而在一般的jQuery回调函数之中,this关键字指的是DOM对象。
  • 在Jquery的实现中,$是一个函数,利用其原型$.propertype承载方法。扩展方式为$.fn.方法名,实现关键语句:$.fn = $.propertype、$.fn.init.property = $.fn。

    RegExp

  • 主要两个方法exec()(捕获组、方向引用)、test()(测试返回布尔值);
  • 其静态属性有两种访问方式,即长属性名和段属性名。每次执行方法都会动态改变静态属性的值。
  • String 类型定义了几个用于在字符串中匹配模式的方法。第一个方法就是match(),在字符串上调用这个方法,本质上与调用RegExp的exec()方法相同。返回一个数组。

    Function

  • 函数的内部属性:arguments、this;
  • 函数的属性和方法—属性:length、prototype;方法:call、apply(在特定的作用域下调用函数,特定的作用域由第一个参数指定,非继承的方法,来之function.prototype);

    This

    在函数中this到底取何值,是在函数真正被调用执行的时候确定下来的,函数定义的时候确定不了。因为this的取值是函数执行上下文(context)的一部分,每次调用函数,都会产生一个新的执行上下文环境。当代码中使用了this,这个this的值就直接从执行的上下文中获取了,而不会从作用域链中搜寻。
  • 箭头函数完全修复了this的指向,this总是指向词法作用域,也就是外层调用者obj
  • 直接函数调用 window
  • 当前调用的对象 xx.yy()
  • new操作符指向新创建的区域
  • 在call/apply时,第一个参数相当于设置函数体内this的值(对象与方法解耦)

    原型

  • 原型对象都是有Object对象创建的。
  • 原生对象和我们自定义对象一样,都存在原型,原生对象的方法都存在原型中,这样我们创建一个新的对象实例时,这些对象就会拥有这些方法。
  • 所有的实例对象都继承了创建它们的构造函数的原型对象。
  • (实例对象会有proto属性)别去纠结有没有这么一个小小的proto属性,我们的思路就是要所有的实例对象能够使用它们的构造函数所指向的原型对象。这个属性是prototype,它是JavaScript标准的一部分。prototype对象默认都有一个constructor属性反向指向了以该prototype对象作为原型的构造函数。
  • prototype对象只为构造函数创建出来的实例对象继承属性所用,构造函数自己却并不使用它(但既然该构造函数自己也是一个对象,那么它也会继承它的构造函数的原型,一般是javascript系统的Function对象)。
  • 默认的prototype对象可以被用户创建的对象替换掉。这样做的话,我们必须重复javascript运行时在幕后对默认的prototype对象所做的那样去手动设置constructor属性。

    function foo() { } ; var f1 = new foo();
    f1.constructor === foo.prototype.constructor === foo
    //replace the default prototype object
    foo.prototype = new Object();
    //now we have:
    f1.constructor === foo.prototype.constructor === Object
    //so now we say:
    foo.prototype.constructor = foo
    //all is well again
    f1.constructor === foo.prototype.constructor === foo

  • 每一个的prototype对象自己都是由Object()函数(默认情况下)创建而来,所以prototype有它自己的原型那就是Object.prototype。因此无论什么类型的实例最终都会继承Object.prototype对象的属性。

  • 沿着上图中的箭头走,你会发现javascript语言核心中的一些有趣的东西:
    • Function.proto指向Function.prototype。这使得:Function.constructor === Function,也就是说:Function是它自己的构造函数!
    • Object instanceof Object == true。这是因为:Object.proto.proto.constructor == Object。注意,跟Object instanceof Object不一样的是Foo instanceof Foo == false,这是因为:Foo并没有作为它原型链上的某个构造函数。
    • Function.prototype.toString是一个内置的函数,它有别于另外一个内置的函数:Object.prototype.toString。
    • Function instanceof Object //true Object instanceof Function //true
  • 虽然可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值。当为对象实例添加了一个属性时,这个属性就会屏蔽原型对象中保存的同名属性;换句话说,添加这个属性只会阻止我们访问原型中的那个属性,但不会修改那个属性。

    深拷贝与浅拷贝

    只是拷贝基本类型的数据,我们把这种拷贝叫做”浅拷贝”。所谓”深拷贝”,就是能够实现真正意义上的数组和对象的拷贝。它的实现并不难,只要递归调用就行了。

    function deepCopy(p, c) {
      var c = c || {};
      for (var i in p) {
        if (typeof p[i] === ‘object’) {
          c[i] = (p[i].constructor === Array) ? [] : {};
          deepCopy(p[i], c[i]);
        } else {
           c[i] = p[i];
       }
      }
      return c;
    }

  • 内置对象

  • 内置对象在所有代码执行之前就已经存在了。
  • eval()方法主要担当一个字符串解析器的作用,他只接受一个参数,而这个参数就是要执行的JavaScript代码的字符串。
  • 函数实际上是Function类型的实例,因此函数也是对象;而这一点正式JavaScript最有特色的地方;由于函数对象,所以函数也拥有属性(length、prototype)和方法(call、apply),可以用来增强其行为;
  • 函数也是个对象,可认为其某一个特殊的键里存放了代码块字符串,而eval可以执行代码块字符串。
  • Object value at left was snapshotted when logged, value below was evaluated just now.这左边的值是log的时候的快照,下面的值是展开的时候计算出来的。

    函数

  • 大家知道小括号的作用吗?小括号能把我们的表达式组合分块,并且每一块,也就是每一对小括号,都有一个返回值。这个返回值实际上也就是小括号中表达式的返回值。所以,当我们用一对小括号把匿名函数括起来的时候,实际上小括号对返回的,就是一个匿名函数的Function对象。因此,小括号对加上匿名函数就如同有名字的函数般被我们取得它的引用位置了。所以如果在这个引用变量后面再加上参数列表,就会实现普通函数的调用形式。
  • 在执行前会进行类似“预编译”的操作:首先会创建一个当前执行环境下的活动对象,并将那些用var申明的变量设置为活动对象的属性,但是此时这些变量的赋值都是undefined,并将那些以function定义的函数也添加为活动对象的属性,而且它们的值正是函数的定义。
  • 在解释执行阶段,遇到变量需要解析时,会首先从当前执行环境的活动对象中查找,如果没有找到而且该执行环境的拥有者有prototype属性时则会从prototype链中查找,否则将会按照作用域链查找。遇到var a = …这样的语句时会给相应的变量进行赋值(注意:变量的赋值是在解释执行阶段完成的,如果在这之前使用变量,它的值会是undefined)。
  • javascript中每个函数都是一个函数对象(函数实例),既然是对象,就有相关的属性和方法。[[scope]]就是每个函数对象都具有的一个仅供javascript引擎内部使用的属性,该属性是一个集合(类似于链表结构),集合中保存了该函数在被创建时的作用域中的所有对象,而这个作用域集合形成的链表则被称为ScopeChain(作用域链)。该作用域链中保存的作用域对象,就是该函数可以访问的所有数据。
  • 函数生命周期分为函数创建和函数调用阶段。(函数调用又可分为建立阶段、执行阶段)。
  • 当开始执行此函数时,就会创建一个Execution Context的内部对象,该对象定义了函数运行时的作用域环境(注意这里要和函数创建时的作用域链对象[[scope]]区分,这是两个不同的作用域链对象,这样分开我猜测一是为了保护[[scope]],二是为了方便根据不同的运行时环境控制作用域链。函数每执行一次,都会创建单独的Execution Context,也就相当于每次执行函数前,都把函数的作用域链复制了一份到当前的Execution Context中)。
  • 当函数执行结束之后,就会销毁Execution Context,也就会销毁Execution Context的作用域链。
  • 每次某个函数被调用,就会有个新的执行上下文为其创建,即使是调用的自身函数,也是如此。
  • 闭包函数创建时的作用域链和其父级函数的执行上下文作用域链相同。
  • 函数调用时的两个阶段(建立阶段、执行阶段):

    *. 建立阶段(发生在当调用一个函数时,但是在执行函数体内的具体代码以前)

    * 建立变量,函数,arguments对象,参数
    * 建立作用域链
    * 确定this的值
    

    *. 代码执行阶段:

    * 变量赋值,函数引用,执行其它代码
    
  • 由于函数可以不局限于任何对象(即与对象具有松散耦合的特点),因此没有理由不在多个对象间共享函数。
        function Person(name) { 
             this.name = name;  
         this.sayName = sayName;
        }
        function sayName() { 
             console.log(this.name);
        }   
    var name = "111"
        var p = new Person("222");
    console.log(sayName());    //111
    console.log(p.sayName());  //222
    
  • 创建对象

  • 按照惯例,构造函数始终都应该以一个大写字母开头,而非构造函数则应该以一个小写字母开头。
  • 构造函数与其他函数的区别,就在于调用他们的方式不同。不过,构造函数毕竟也是函数,不存在定义构造函数的特殊语法。任何函数,只要通过new操作符来调用,那它就可以作为构造函数;而任何函数,如果不通过new操作符来调用,那它跟普通函数也不会有什么两样。
  • 有两种方式使用in操作符:1、单独使用和在for-in循环中使用。在单独使用时,in操作符会在通过对象能够访问给定属性时返回true,无论该属性存在于实例中还是原型中。2、使用for in循环,返回的是所有能够通过对象访问的、可枚举的属性,其中既包括实例中的属性,也包括存在于原型中的属性。如果实例中的属性屏蔽了原型中不可枚举的属性,那么也会返回。IE9之前的版本实现上有一个Bug,屏蔽不可枚举属性的实例属性不会在for-in中返回。
  • ECMAScript中描述了原型链的概念,并将原型链作为实现继承的主要方法。 其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。
  • 假如我们让原型对象等于另一个类型的实例,结果会如何呢? 显然,此时的原型对象将包含一个指向另一个原型的指针,相应的,另一个原型中也包含着一个指向另一个构造函数的指针。 假如另一个原型又是另一个类型的实例,那么上述关系依然成立,如此层层递进,就构成了实例与原型的链条。 这就是所谓原型链的基本概念 。
  • 实现原型链有一种基本模式,其代码大致如下:
      function SuperType() {
      this.flag = true;
    }
    SuperType.prototype.getSuperFlag = function() {
      return this.flag;
    };
        function SubType() {     
      this.subFlag = false;
    }
    //继承了SuperType  先确立了继承关系之后,又添加了方法 
    SubType.prototype = new SuperType();
    //注意添加方法的方式与位置
    SubType.prototype.getSubFlag = function() { 
     return this.subFlag;
     };
     var st = new SubType();
     alert(st.getSuperFlag());  //true
     //测试继承关系
     alert(st instanceof Object);  //true
     alert(st instanceof SuperType);  //true
     alert(st instanceof SubType);  //true
     alert(Object.prototype.isPrototypeOf(st));  //true
     alert(SuperType.prototype.isPrototypeOf(st));  //true
     alert(SubType.prototype.isPrototypeOf(st));  //true
    
  • 对上面代码的总结:

    *1、代码的顺序以及继承关系的测试结果。    
    *2、继承是通过创建SuperType的实例,并将该实例赋给SubType.prototype实现的。
        实现的本质是重写原型对象,代之以一个新类型的实例。
    
  • 确定原型和实例的关系:可以通过两种方法来确定原型和实例之间的关系。第一种方式是使用instanceof操作符;第二种方法是使用isPrototypeOf()方法。

  • 虽然可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值。
  • 通过原型链实现继承时,谨慎地定义方法:子类型有时候需要重写超类型中的某个方法,或者需要添加超类型中不存在的某个方法。但不管怎样,给原型添加方法的代码一定要放在替换原型的语句之后。 同时不能使用对象字面量创建原型方法。因为这样做就会重写原型链。
  • JavaScript 主要通过原型链实现继承。原型链的构建是通过将一个类型的实例赋值给另一个构造函数的原型实现的。这样,子类型就能够访问超类型的所有属性和方法,这一点与基于类的继承很相似。原型链的问题是对象实例共享所有继承的属性和方法,因此不适宜单独使用。解决这个问题的技术是借用构造函数,即在子类型构造函数的内部调用超类型构造函数。这样就可以做到每个实例都具有自己的属性,同时还能保证只使用构造函数模式来定义类型。使用最多的继承模式是组合继承,这种模式使用原型链继承共享的属性和方法,而通过借用构造函数继承实例属性。
  • 组合继承是javaScript最常用的继承模式;不过,它也有自己的不足。组合继承最大的问题就是无论什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。没错,子类型最终会包含超类型对象的全部实例属性,但我们不得不在调用子类型构造函数时重写这些属性。

    匿名函数

  • 因为闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存。
  • 闭包作用域链的这种配置机制引出了一个值得注意的副作用,即闭包只能取得包含函数中任何变量的最后一个值。解决方法:在外面套一层立即执行函数并传递参数,增加一层作用域,从而各自产生自己的变量副本。
  • 在闭包中使用this对象也可能会导致一些问题。我们知道,this对象是在运行时基于函数的执行环境绑定的:在全局函数中,this等于window,而当函数被作为某个对象的方法调用时,this等于那个对象。不过,匿名函数的执行环境具有全局性,因此其this对象通常指向window。解决方法:在定义匿名函数之前,定义 var that = this;即在包含函数中特意声明一个变量来保存this
  • JavaScript 从来不会告诉你是否多次声明了同一个变量;遇到这种情况,它只会对后续的声明视而不见(不过,它会执行后续声明中的变量初始化)
  • 模仿块级作用域(通常成为私有作用域)的匿名函数的语法如下:

    (function () { //这里是块级作用域
    })();

  • 对上面代码的总结:
    • 1、以上代码定义并立即调用了一个匿名函数。将函数声明包含在一对圆括号中。表示实际上是一个函数表达式。而紧随其后的另一对圆括号会立即调用这个函数。
    • 2、这种做法可以减少闭包占用的内存问题,因为没有指向匿名函数的引用。只要函数执行完毕,就可以立即销毁其作用域链了。
  • 当在函数内部定义了其他函数时,就创建了闭包。闭包有权访问包含函数内部的所有变量。当函数返回了一个闭包时,这个函数的作用域会一直在内存中保存到闭包不存在为止。
  • 只要new表达式之后的constructor返回(return)一个引用对象(数组,对象,函数等),都将覆盖new创建的匿名对象,如果返回(return)一个原始类型(无return时其实为return原始类型undefined),那么就返回new创建的匿名对象。

Comments

去留言
2016-03-29

⬆︎TOP