1、理解对像
var person = {name:'nic'}
1.1 属性类型

  1.1.1 数据属性
      Configurable  delete  true
      Enumerable    for in  true  能否通过for-in循环返回属性  true能 | false不能
      Writeable     修改    true
      Value         写入    undefined
      
      Object.defineProperty( person, 'name' , { Writable:false,value:'pill' }  )
   1.1.2访问器属性
      读访问:getter 写访问:setter
        configurable    delete true
        Enumerable      for in true
        Get             undefined     只定义,证明只能读 不能写
        Set             undefined     只定义,证明只能写 不能读
        访问器属性不能直接定义,须使用Object.defineproperty()
                var person = { _name:'pill' }  //定义对像访问属性
                Object.defineProperty(person,'name',{ get:function(){ return this._name }; })  //设置访问器
                person.name  //访问,即相当于访问name的get函数,直接person._name也可得到相同结果,这样做访问器就无意义了
                //即设置 person.name访问器来访问 person._name,相当于加减权限
    1.1.3 定义多个属性
        Object.defineProperties( person,{ _year:{value:2004}, ) 
        var book = {}  Object.defineProperties( book,{ year:{value:2004},edition:{value:2},Year{ get:function(){return this.year},set:function(nv){if(nv > 2004){this.year = nv; this.edition += nv -2004;}} } } )  //year | edition 数据属性,Year :访问器属性
     1.1.4 读取属性特性
        Object.getownPropertyDescriptor( 属性所在对像, 读取其描述符的属性名称 ) 给定属性描述

2 创建对像

2.1工厂模式
  function createPerson( name,age,job){
      var o = new Object;
      o.name = name;
      return o;
  }
  var person1 = createPerson( 'nic' )
  无法解决;如何识别一个对像的类型;
  
 2.2构造函数模式   ---创建对像的函数
 function Person ( name,age,job ){
      this.name = name;
      this.age = age; 
      this.sayName = function() {
        alert(this.name);
      }
 }
 var p1 = new Person( 'nic','29','software Engineer' ) //  p1保存着Person的一个实例;会有一个constructor的属性 ;指向Person                          console.log(p1.constructor == Person5); //true
 //好处:可以将它的实例标记为 一种特定的类型;
 var person = new Person( 'nic','29','software Engineer' ) person.sayName()  //构造fn使用
 Person( 'nic','29','software Engineer' ) window.sayName(); //普通fn  属性与方法都加给window对象
 var o = new Object(); Person.call(o,'kristen',25,'software Engineer')  //另一个对象的作用域中调用  obj.call(o,) this指向o,否则在作用域中,this会指向window; 
 
 缺点:每个p1都会有一个sayName()的方法,不是同一个Function的实例,逻辑上讲,构造fn可以定义为:
 
      function Person ( name,age,job ){
      this.name = name;
      this.age = age; 
      this.sayName = new Function(){ alert( this.name ) }  //与声明是等价的; 
 }
 会导致不同的作用域链和标识符解析不同,不同实例的上的同名函数是不相等的,以下代码可证明这一点:
 alert( p1.sayName == p2.sayName2 ) //false;
 改进:
      function Person ( name,age,job ){   //全局作用域中定义
      this.name = name;
      this.age = age; 
      this.sayName = sayname; 
 }
 function sayname(){ alert( this.name ) } 
 
 //问题: 全局作用域中定义的fn只能被某个对象调用,名不事实,如果对象需要定义很多方法,则需要定义多个全局fn,无封装性可言了,
 //所以,有了原型!
 
 2.3 原型模式 
    创建的每一个函数都有一个 prototype(原型)属性  [是一个指针,指向一个对象] 
    用途:包含 可以由特定类型的所有实例共享的属性和方法;
    定义 :通过调用构造函数而创建的那个对象实例【比如 p1,是由构造Person创建的对象实例】  的原型对象
    好处 :可以让所有对象实例共享它所包含的属性的方法;如p1/p2/p3/p4/.../pn,  所有对象都能共享原型中的属性和方法
    
    function Person(){} //什么都不写
    Person.prototype.name = 'nic'
    Person.prototype.age = 31
    Person.prototype.sayName = function (){ alert(this.name) }
    var p1 = new Person();
    var p2 = new Person();
    p1.sayName(); //nic
    p2.sayname(); 
    alert( p1.sayName == p2.sayName2 ) //这次是true了;好处显而易见,不需要重新创建;
    
    2.3.1 理解原型对象
      function Person() {} //创建新函数,即会同时建立,fn------>> prototype 属性 ---->>指向函数的原型对象 Person
                                        原型对象获得constructor(构造函数)属性  ---...->>ptototype所在的函数的指针
                                        Person.prototype.constructor ----> person
                                        
console.log(Person);  //function Person()
console.log(Person.prototype)   //Object {name: "nic", age: 31, job: "software Engineer"}
console.log(Person.prototype.constructor);  //function Object()

function Person(){}
         -----------------------------------------------|     
         ||                                             |
----------------                                        |
Person         |                 Person Prototype       |
prototype      |  ------------== constructor     --------
----------------                    ||
                                    |
---------------                     |
创建person1   |                     |
[prototype]   |----------------------
---------------

1、 function Person(){} //创建一个fn,生成一个prototype----------指向---------> Person Prototype (即 2)

2 Person.prototype.name = 'nic'

  Person.prototype.age = 31
  Person.prototype.sayName = function (){ alert(this.name) }
//以上定义原型属性与方法,生成一个constructor-----------------指向-----------> Person 函数  (即 1)

3、 var p1 = new Person();

   var p2 = new Person();
//以上创建实例对象,生成一个 [prototype] --------------指向-------------->Person Prototype(即原型属性和方法 即2)

重写原型之后:

   
function Person(){}
         -----------------------------------------------|     
         ||                                             |
----------------                                        |
Person         |            new  Person Prototype       |
prototype      |  ------------== constructor     --------
----------------                 name   nic
                                 age    29
---------------                     
创建person1   |                     
[prototype]   |--------------== Person Prototype
---------------                  constructor 

创建person1  中的[ptototype]   重写对象切断了现有原型与任何之前存在的对象实例之间的联系,它们引用的仍然是最初的原型。
----------------------------------------------------------------------------------------------------------------------------------
2.3.2原型in的方法

var zzp1 = new Person();
console.log('in通过原型: '+('name' in zzp1));   //in通过原型或对象都返回true; 
zzp1.gg = 'gg'
console.log('in通过对象:'+('gg' in zzp1));
console.log(zzp1.hasOwnProperty(name)); //false; //false 或 not defined;           
console.log(zzp1.hasOwnProperty(gg)); // not defined; 
结论: 如果hasOwnProperty()方法返回false && in返回true ==== 该属性在原型里

function hasOwnPropertyOrNot(Obj,name){ return !Obj.hasOwnProperty(name) && (name in Obj); }

对象属性可枚举,表示该属性的值不可修改,可认为该属性是常量。
for-in返回所有能通过对象访问、可枚举属性
屏蔽了原型中不可枚举属性的实例属性也会在for-in中返回
所有开发人员定义的属性都是可枚举的

enumerable:true 表示可枚举,即能用for-in显示出来,
enumerable:false 表示不可枚举,即无法用for-in显示出来 
----------------------------------------------------------------
var obj2 = {name:'jack',age:23}
Object.defineProperty(obj2,'id',{
    value:'123',
    enumerable:true    //若定义不可枚举只需把 enumerable:false 即可
})

for(var po in obj2){
    if(po == 'id'){
        alert('  enumerable : true,表示可枚举,如:enumerable : false 表示不可枚举,即不能用for-in显示出来,当然这句也不会显示 ')
    }
}

IE8 有bug

获取对象上所有可枚举的属性[代替for-in的方法];
    function  aap() {
}
aap.prototype.name = 'aap name'
aap.prototype.age = 29;
aap.prototype.job = 'software Engierre'
aap.prototype.sayName = function () {
    alert(this.name)
}
var keys = Object.keys(aap.prototype);  // 通过原型调用,即fon-in出来的,数组 'name' 'age' 'job'  'sayName'
alert(keys)

var aap1 = new aap();
aap1.name = 'rob'
aap1.age = 30;

var aap1keys = Object.keys(aap1);  //通过实例调用
alert(aap1keys)   //'name' 'age'

//所有实例办法 :
var keyss = Object.getOwnPropertyNames(aap.prototype);
console.log(keyss);  //["constructor", "name", "age", "job", "sayName"]
//包括不可枚举的constructor属性
//可用来代替for-in

2.3.3
function Ps1(){}
var fri = new Ps1();  //重点注意: 先创建实例,后调用重写的原型才出错,如果此句放在重写原型之后则是正常的,不会出错。
Ps1.prototype = {
  name:'nnick',
  age:29,
  job:'software Eng',
  sayName:function(){
  console.log(this.name);

}
}
重写之后,constructor不再指向Ps1, 相当于完全重写了prototype对象 ,因此constructor变成新对象的constructor属性,指向obj构造fn;

console.log(fri instanceof Object); //true;
console.log(fri.constructor == Ps1); //false;

以下调用都出错:
console.log(fr1.name);
console.log(fr1.age);
console.log(fr1.job)
fr1.sayName(); //is not defined fri指向的原型对象不包含以该名字命名的属性

//调用构造fn时会为实例添加一个指向最初原型的prototype指针;
//原型修改成另一对象 == 切断构造fn 与 最初原型的联系;
//实例中的指针仅指向原型,不指向构造fn;

2.3.5 原生对象的原型
String.prototype.startWith = function(text){
return this.indexOf(text) == 0;
}
var msg = 'hello world!'; 
alert(msg.startWith('hello'));  //true

2.3.6原型对象的问题
  引用类型的属性问题
  function Ps2(){}
  Ps2.prototype = {
    constructor:Ps2,
    name:'iiiccc',
    age:29,
    friends:['f1','f2'],
    sayName : function () {
        console.log(this.name);
    }
}
    
var Ps2_1 = new Ps2();
var Ps2_2 = new Ps2();
Ps2_1.friends.push('van')         //本意上想只修改 Ps2_1,
console.log(Ps2_1.friends);
console.log(Ps2_2.friends);       //结果调用的Ps2_2  出问题了

2.3.7 组合使用构造fu 原型模式
    function ps3(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ['f3','f4']
}
ps3.prototype = {
    constructor:ps3,
    sayName:function(){
        console.log(this.name);
    }
}

var ps31 = new ps3('p333','31','sfineer')
var ps32 = new ps3('p222','32','sfirry')

ps31.friends.push('van')
console.log(ps31.friends);
console.log(ps32.friends);
console.log(ps31.friends === ps32.friends);
console.log(ps31.sayName === ps32.sayName);

2.3.8 动态原型模式

    function ps4(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    if (typeof this.sayName != 'function') {  //如果没有sayName这个fn,才会将它添加到原型中
        ps4.prototype.sayName = function () {
            console.log(this.name);
        }
    }
}

var fr1 = new ps4('ps4name',32,'softEngiir')
fr1.sayName()

2.3.9 寄生构造fn模式
    function ps5(name, age, job) {   //创建一个新的对象,初始化属性和方法
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function () {
        console.log(this.name);
    }
    return o;         //再返回这个对象    ,另构造fn不回返值的情况下,默认会返回新对象的实例。
}
var fr2 = new ps5('fr2name',33,'ef')
fr2.sayName()

    function specialArray() {
    var values = new Array();             //创建数组
    values.push.apply(values,arguments)   //添加值
    values.toPipedString = function () {  //添加方法
        return this.join('|')             
    }
    return values;        //返回
}
var colors = new specialArray ('red','blue','green');
console.log(colors.toPipedString());

2.3.10 稳妥对象
  没有公共属性,不引用this new  
  与上区别
  function Pa1(){
    var o = new Object();
    o.sayName = function(){
        console.log(name);
    }
    return o;     
  }
  
  3.1 继承
    3.1.2 原型链
    function A(){ this.property = true; }
    A.prototype.AsayName = function() { return this.Aproperty;  }  //定义A原型方法
    
    function B(){ this.property = false; }
    B.prototype = new A()  //继承A 创建A实例,并将该实例赋给B.prototype实现,重写原型对象
    B.prototype.BsayName = function (){ return this.Bproperty }  //B 继承了A,A继承了object
    
    B.prototype.Asayname = function(){ return false; }  //重写A原型 重写超类型的方法
    
    //Asayname 已在原型中出现,如果是 A 调用则是原来的方法,
    //如果是用B调用则是重新定义的方法,注意顺序 。
          
    //最后验证
    var T = new B();
    console.log( T instanceof Object ) //true
    console.log( T instanceof A )     //true
    console.log( T instanceof B )     //true
    
    3.1.2 原型链的问题
          1、引用类型问题
            function A(){
            this.colors = ['red','yellow','green']    //引用类型的
            }
            function B() {
            }
            B.prototype = new A() //B 继承A
            var inst = new B();
            inst.colors.push('black');
            console.log(inst.colors);
            var inst2 = new B()
            console.log(inst2.colors);  //出问题了
            2、创建子类,不能向超类型的构造fn中传递参数;
      3.1.3 借用构造fn
            子类型内部调用超类型的构造fn
              function A() {
              this.colors = ['red','yellow','green']  //引用类型的
              }
              function B() {
                  //继承A
                  A.call(this);   //为下面的var inst = new B();调用A;即会在B对像执行A()函数中定义的所有对象初始化
              }
              var inst = new B();
              inst.colors.push('black');
              console.log(inst.colors);
              var inst2 = new B()
              console.log(inst2.colors);  //正常分开,inst2不用被引用到
              //传参
                function A(name){
                this.name = name;
            }
            function B(){
                A.call(this,'nnniiiccc')
                this.age = 29;
            }
            var inst = new B();
            alert(inst.name);
            alert(inst.age);

-------------- 组合继承-- 伪经典继承------------------------------------------

        不足:无论什么情况下,都会计用两次超类型的构造函数,一次是在创建子类型原型的时候,另一次子类型构造函数内部,{子类型最终会包含超类型对象的全部实例属性,但我们不得不在调用子类型构造函数时重写这些属性}
                function A(name) {
                    this.name = name;
                    this.colors = ['red','blue','green']
                }
                A.prototype.sayName = function () {
                    console.log(this.name);
                }

                function B(name,age) {
                    A.call(this,name);
                    this.age = age;
                }

                B.prototype = new A()    //B的实例赋给A的原型
                B.prototype.sayAge = function () {
                    console.log(this.age);
                }
                var inst = new B('nnic',29)  
                inst.colors.push('black');
                console.log(inst.colors); //["red", "blue", "green", "black"]
                inst.sayName() //nnic
                inst.sayAge(); //29

                var inst2 = new B('grey',28)
                console.log(inst2.colors); //["red", "blue", "green"]
                inst2.sayName() //grey
                inst2.sayAge()  //28

--------------------原型式继承-----------------------------------------------------

                    function object(o) {
                    function F() {    //创建临时构造Fn
                    }
                    F.prototype = o;  //传入的对象作为临时构造fn的原型
                    return new F();   //再返回空上临时类型的一个实例
                  }  //本质上对传入的对象进行浅复制

                  var person = {
                      name:'nichol',
                      friends:['f1','f2','f3']
                  };
                      var anotherPerson = object(person)  //person传给object对象,返回:新对象,将person作为原型, person.friends ===person所有 也会被下面的共享
                      anotherPerson.name = 'grey'
                      anotherPerson.friends.push('rob')

                      var yetanotherperson = object(person)
                      yetanotherperson.name = 'yet'
                      yetanotherperson.friends.push('yetpush')

                      console.log(person.friends);//["f1", "f2", "f3", "rob", "barbie"]
                      与Object.create()方法一样
                      var person = {
                                  name:'nichol',
                                  friends:['f1','f2','f3']
                      };
                      var anotherPerson = Object.create(person,{
                        name:{
                            value:'grey'
                        }
                      })
                      console.log(anotherPerson)  //grey

----------------------寄生式继承------------------------------------------

                      function A(name){
                        this.name =name;
                        this.colors = [ 'red','blue','yellow'];
                      }
                      A.prototype.sayName = function(){ console.log(this.name) };
                      
                      function B(name,age){ A.call(this.name); this.age = age; }   // 第二次调用
                      
                      B.prototype = new A(); //第一次调用
                      B.prototype.constructor = B;    
                      B.prototype.sayAge = function (){ console.log(this.age) }
                      
               function  inheritPrototype(B,A) {
               var prototype = Object(A.prototype) //创建对象  创建越类型原型的副本
               prototype.constructor = B;          //增强对象  添加constructor属性,弥补因重写原型失去的默认constructor属性
               B.prototype = prototype             //指定对象  新创建对象(即副本) 赋值给子类型的原型
              }
              
              // function inheritPrototype(子类型构造函数, 超类型构造函数) 
              

7、函数表达式


function AA(i,n,k) {}console.log(AA.name); //AA
function jc(num){ if(num<=1){return 1;}else{ return num * jc(num-1) } } console.log( jc(4); )//递归阶乘

function jc(num){ if(num<=1){return 1;}else{ return num * arguments.callee(num-1) } } console.log( jc(4); )//递归阶乘  arguments.calle 表示一个
指向正在执行的函数的指针;

推荐写法:( 命名函数表达式 )
        var factorial = (function f(num) {
        if (num <= 1) {
            return 1;
        }
        else {
            return num * f(num - 1);
        }
    })
    不影响:
    var anotherjc= factorial;
    factorial = null;  //清空变量
    console.log(anotherjc(4));

7、2 闭包

      闭包指有权访问另一个函数作用域中的变量的函数。 创建闭包常见的方式:一个函数内部创建另一个函数
      function ccf(propertyName){
        return function (obj1,obj2){  //匿名函数作用域链中包含ccf()的作用域;
          var v1 = obj1[propertyName]
          var v2 = obj2[propertyName]
          if(v1<v2){
          return -1;
          }
          else if(v1>v2){
          return 1;
          }
          else{
          return 0;             
          }
        }
      }
              function a() {
              var i=0;

              function b() {    //1/函数a的内部函数b
                  console.log(++i);
              }
              return b  //这里是重点
              }
              var c = a()  //2、被函数a的外部变量引用时,就创建了一个闭包
              c()
  闭包:a执行完后,javascript垃圾回收机制GC不会收回a所占用的资源 [ why ? 因为a的内部函数b的执行需要依赖a中的变量 ]
        a始终存在,再执行c()时,i会自动+1
              function a() {
              var i=0;

              function b() {    //1/函数a的内部函数b  ab只是相互引用,不受外界打扰,ab会被GC回收
                  console.log(++i);
              }
          //    return b  这里b没有返回,只是引用
              }
              var c = a()  //2、TypeError: c is not a function
              c()

-----------------------------闭包微观世界----------------------------------

  概念: 函数的执行环境(excution context)、活动对象(call object)、作用域(scope)、作用域链(scope chain)
              
              function a() {      //js解析器会将a() 的作用域链(scope chain) == a时a所在的 '环境'  如果a()是全局,则(scope chain) 只有window对象
              var i=0;

              function b() {    //
                  console.log(++i);
              }
              return b  //
              }
              var c = a()  //  执行a(),进入执行环境(excution context) ,创建执行环境过程: 1、a.scope = a (为a加scope属性,即a的作用域)
                                                                                           2、创建活动对象( call object ) (不具原型不能通过js代码访问);
                                                                                           3、活动对象( call object ) 添加到 a的(scope chain) 最顶端,此时a的(scope chain)
                                                                                              有两个对象: a的(call object) 和 window 对象
                                                                                           4、活动对象( call object ) 添加 arguments 属性 保存调用a所传的参数
                                                                                           5、再把a() 的形参和内部b()的引用也添加到a的 ( call object )上 完成b()定义
                                                                                              因此,函数b的作用域链被设置为b所被定义的环境,即a的作用域。
                                                                                           
              c()
              a 返回 b的引用 给c , 又b的作用域包含对函数a的活动对象引用,也就是说b可以访问到a中定义的所有变量和函数         ,函数b被c引用,函数b又依赖函数a,因此函数a在返回后不会被GC回收。
              
              call object                                    call object                                               
              实参arguments                                  实参arguments
              形参parameters        b的prototype原型对象     形参parameters                     windows对象
              内嵌function                                   内嵌 function
              内部变量variables                              内部变量variables
              
              理解作用域链中包含哪些活动对象??
              
              

7.2.1 闭包与变量

闭包只能取得包含函数中任何变量的最后一个值,

    function cf(){
        var result = new Array();
        for( var i=0;i<10;i++ ){
            result[i] = function(){
                return i;
            }
        }
        return result;
    }

var funcs = cf();
for(var i=0;i<funcs.length;i++){
  console.log(funcs[i]());
}//每次返回都是10 ,因为每个函数的作用域链中都包含着cf()函数的活动对象,所以它们引用的都是同一个变量i。当cf()函数返回后,变量i的值就是10,此时每个函数都引用着保存变量i的同一个变量对象,所以每个函数返回后都是10

    function cf(){
        var result = new Array();
        for( var i=0;i<10;i++ ){
            result[i] = function(num){  //没有直接赋值给数值;定义一个匿名fn;
                return function () {
                    return num;    //有创建并返回一个访问 num的闭包,即result数组中每个函数都有自己num变量的副本,因此可以返回不同的值
                }
            }(i) //传入变量i 会把奕量i的当前值给num;
        }
        return result;
    }

    var funcs = cf();
    for(var i=0;i<funcs.length;i++) {
        console.log(funcs[i]());
    }

7.2.2 this 变量

    var name = 'the window'
    var obj = {
        name: 'my ojb',
        getNameFun:function () {
          return function () {
            return this.name;
        }
    }
    }
    console.log(obj.getNameFun()()); // the window   getnamefun是返回一个函数,因此调用getNamefunn()()会立即调用它返回的函数,结果就是一个字符串,
//为什么没有取得其包含作用域或外部作用域的this对象呢;?
//每个函数被调用时,其活动对象都会自动取得这两个特殊变量,this arguments ,内部函数在搜索这两个变量时,只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个

//外部作用域中的this对象保存在一个闭包里,即能访问到变量里,就可以让闭包访问该对象
var name1= 'the windows'
var obj2 = {
    name : 'my obj',
    getNameFun: function () {
        var that = this;  //obj2中的this保存在这里
        return function () {
            return that.name
        }
    }
}
console.log(obj2.getNameFun()()); //my obj;
   var name2 = 'this window3'
    var obj3 = {
        name2:'my obj3',
        getName: function () {
            return this.name2;
        }
    }
    console.log(obj3.getName());    //my obj3
    console.log(  (obj3.getName)()  );  // my obj3
    console.log(  (obj3.getName = obj3.getName)()  );  //the window

7.2.4 内存泄漏 IE

function ag(){
  var elem = document.getElementById('someElement')
  elem.onclick = function(){
      alert(elem.id)
    }
}
解决
function ag(){
  var elem = document.getElementById('someElement')
  var id = elem.id
  elem.onclick = function(){
      alert(id)
    }
    elem = null;
}

7.3 私有变量
访问私有的公有方法称为 特权方法 privileged method 两种对像上创建
1、构造函数中定义

      function Myobj(){
          var privateVariable = 10 //私有变量
          function privateFun() { //私有方法 
            return false;
          }
          this.publicMethod = function (){  //特权方法 
              privateVariable++;      //访问私有变量
              return privateFun();    //调用私有方法
          }
      }
      example:
      
      function Person(name){
        this.getname = function (){   //特权方法
          return name;
        }
        this.setname = function (value){ //特权方法 
          name = value;
        }
      }
      (老话题,每次调用时都会生重新创建这两个方法 ,在构造函数中定义特权方法也有一个缺点:必须使用构造函数模式来达到这个目的),所以使用静态私有变量来实现特权方法即可避免这个问题

7.4.1静态私有变量

        (function (){  //创建私有作用域------------注意:1、使用函数表达式,不使用函数声明,函数声明只能创建局部函数
          var privateVariable = 10 //私有变量
          function privateFun() { //私有方法 
            return false;
          }
          Myobj = function(){ //---------------同理,在此,2、声明myobj时也不使用var 否则会变成一个局部函数
          }; //构造函数
          
          //公有特权方法  体现原型模式
          myobj.prototype.publicMethod = function (){  //特权方法 
              privateVariable++;      //访问私有变量
              return privateFun();    //调用私有方法
          }
      })()
      
(function () {
    var name = ''    //2 变成静态,由所有实例共享的属性,
    Person = function (value) {    //1 构造fn与getname() setname()方法一样,都有权访问私有变量name
        name = value;
    };
    Person.prototype.getName = function () {
        return name;
    };
    Person.prototype.setName = function (value) {
        name = value;
    };
})();

var p1 = new Person('nicnewp1');
alert(p1.getName());
p1.setName('setnamep1');   //3 在一个实例上调用setname() 会影响所有实例
alert(p1.getName());

var p2 = new Person('michwp2');
alert(p2.getName());
alert(p2.getName());

7.4.2 模块模式

前面:为自定义类型创建私有变量和特权方法
模块模式: 为单例创建私有变量和特权方法 ,只有一个实例的对象  js以对象字面量的方式来创建单例对象

var singleton = {
  name:value,
  method:function(){
    
  }
}
var singleObj = function () {  //1 返回对象的匿名函数
    //2 私有变量和私有函数
    var privateVariable = 10;
    function privateFun() {
        return false;
    }
    //公有方法和属性
    return{   //返回的对像只有公有函数属性和方法
        publicProperty:true,
        publicMethod : function () {
            privateVariable++;
            return privateFun();
        }
    }
}()  
----------------------------------------
var application = function () {  //管理组件的application对象
    // 私有变量和私有函数
    var components = new Array();
    // 初始化
    components.push(new Basecomponent());//向数组中添加Basccomponent新实例

    return {//返回对象的get...regi..方法
        getComponentCount: function () {
            return components.length;
        },
        registerComponent: function (component) {
            if(typeof component == 'object'){
                components.push(component)
            }
        }
    }
}()
//必须 创建obj,需要初始化,还有公开一些访问私有数据的方法,可使用模块模式;单例作为全局对象存在,不会将它传递给一个fn,

7.4.3 增强的模块模式 
    var singleObj = function () {  //1 返回对象的匿名函数
    //2 私有变量和私有函数
    var privateVariable = 10;
    function privateFun() {
        return false;
    }
    //创建对象
    var object = new CustomType();  //返回的对象之前加入对其增强的代码,适合那些单例必须是某种类型的实例 同时必须添加某些属性 方法对其加以增强的情况
    
    //添加特权公有方法和属性
    object.publicProperty = true;
    object.publicMethod = function(){
            privateVariable++;
            return privateFun();
    }
  return object;  //再返回这个对象 
}();

总结: 函数表达式,拉姆达函数  不需要名字,匿名函数
        无法确定如何引用函数时,递归函数全变得比较复杂
        递归应始终使用arguments.callee来调用自身,不要用函数名
        当函数内部定义了其它函数时,就创建了闭包  
            后台执行时,闭包的作用域链包含自己、函数、全局作用域
            通常,函数的作用域、所有变量都会在函数执行结束后销毁
            但,当函数返回一个闭包时,此函数的作用域链接将一直存在内存中直到闭包不存在为止
            创建并立即调用一个函数,可执行其中代码,又不会在内在中留下引用 
            结果即函数内部所有变量都会被销毁,除非将某些变量给包含作用域中的变量
         闭包还可用于在对象中创建私胡变量
            即使js中没有正式的私有对象概念,但可使用闭包实现公有方法 【此方法可访问包含作用域中的定义变量】
    
    
        console.log(box.nodeName);
console.log(box.nodeType);
console.log(box.nodeValue);
console.log(box.nodeList);
var mylist = document.getElementsByTagName('ul')[0]
var deeplist = mylist.cloneNode(true);
console.log(deeplist);
console.log(mylist.cloneNode());
console.log(mylist.children[0].nextElementSibling);
console.log(mylist.children[0].previousElementSibling);
console.log(mylist.children[0] == mylist.children[mylist.children.length - 1]);
console.log(mylist.childNodes[0].nodeType);
console.log(mylist.hasChildNodes());
console.log(mylist.getElementsByTagName('li')[0].hasChildNodes());
console.log(mylist.ownerDocument == window.document);
var html = document.documentElement;
console.log(html == document.childNodes[0]);
console.log(html == document.firstChild);
console.log(document.URL);
console.log(document.domain);
console.log(document.referrer);
console.log(window.location.href);
console.log(box.id);
console.log(box.className);
console.log(box.title);
console.log(box.lang);
console.log(box.dir);
html5 规范 自义定 data- 
<input type='text' data-username='elmoo' />
getAttribute
setAttribute
attributes

inp.attributes.getNamedItem('id').nodeValue = 'inp2';
console.log(inp.attributes.getNamedItem('id').nodeValue+'123');
console.log(inp.attributes.item(0));
document.createElement()
  不能动态设置<iframe> name特性
  不能reset()方法重置
  动态创建相同name的单选按钮毫无联系
  

var txt1 = document.getElementById('txt1')
console.log(txt1.childNodes[0].nodeType);
console.log(txt1.childNodes[0].nodeValue);

var txt1 = document.getElementById('txt1')
console.log(txt1.childNodes[0].nodeType);
console.log(txt1.childNodes[0].nodeValue);

var txtNode = document.createTextNode(" 1 、hello")
var antxtNode = document.createTextNode(' 2、anther yippe')
txt1.appendChild(txtNode);
txt1.appendChild(antxtNode)
//合并文本节点,3---> 1
txt1.normalize();

//分割文本节点,splitText(12)从第12位开始;
var newNode = txt1.childNodes[0].splitText(12)
console.log('分割一: ' + txt1.childNodes[0].nodeValue);
console.log('分割二:' + txt1.childNodes[1].nodeValue);

//在文本中间插入数据
var newtxtN = document.createElement('strong')
var newtxtV = document.createTextNode('oooooo')
newtxtN.appendChild(newtxtV);
txt1.insertBefore(newtxtN, txt1.childNodes[1])
var ii = 0;
for (var i = 0; i < txt1.childNodes.length; i++) {
    if (txt1.childNodes[i].nodeType == 3) {
        ii++
    }
}
console.log('总文本节点个数: ' + ii);
    var table = document.createElement('table')
    var tbody = document.createElement('tbody')
    table.appendChild( tbody )

    tbody.insertRow(0);
    tbody.rows[0].insertCell(0);
    tbody.rows[0].cells[0].appendChild( document.createTextNode('c1c1c1c') )
    tbody.rows[0].insertCell(1);
    tbody.rows[0].cells[1].appendChild( document.createTextNode('c12c1c12') )

    tbody.insertRow(1);
    tbody.rows[1].insertCell(0);
    tbody.rows[1].cells[0].appendChild( document.createTextNode('c3c13c3') )
    tbody.rows[1].insertCell(1);
    tbody.rows[1].cells[1].appendChild( document.createTextNode('c4c4c4c4') )
    

NodeList NameNodeMap HTMLCollection三个集合都是动态存在,每当文档结构发生变化时,它们都会得到更新,因此,它们始终保存着最新,最准确的信息;

    三者都是类数组
    
    HTMLCollection:返回HTMLCollection类型集合,如document.forms document还有images, applets, links, forms, anchors也返回HTMLCollection类型等
    NodeList:节点集合
    NamedNodeMap:特性集合
    
var boxs = document.getElementsByTagName('div') //HTML集合
console.log(boxs instanceof  HTMLCollection);

var boxchild = box.childNodes;//节点
console.log(boxchild instanceof NodeList);

var attrs = box.attributes;//特性
console.log(attrs instanceof NamedNodeMap);

DOM扩展
1、选择符API 

1、1 Selectors API 支持CSS查询
  querySelector()[匹配第一个找到的元素]  querySelectorAll() [返回NodeList集合实例]  
  matchesSelector() 方法 支持较少
1、2元素遍歷
  obj.childElementCount
  obj.firstElementChild
  obj.lastElementChild
  obj.previousElementSibling
  obj.nextElementSibling
  
1、3HTML5
  var cla = document.getElementsByClassName('cla');
  console.log(cla.length);
  var box = document.getElementById('box');
  var classname = box.className.split(' '); //拆分成數組
  var pos = -1;
  var i;
  var len;
  for(i=0,len=classname.length;i<len;i++){
      if(classname[i] == 'box1'){
          pos = i;  //找到匹配則停止搜索,此時i在匹配的位置上
          break;
      }
  }
  classname.splice(i,1); //刪除i位置開始1個值
  box.className = classname.join(' ');    

  屬性 classList 是 新集合類型 DOMTokenList 的 實例
  DOMTokenList有一個表示自己包含多少元素的length屬性,
  add(value);
  contains(value);
  remove(value);
  toggle(value);
  前面一大堆代碼可用一句話替代 :box.classList.remove('box1');   //IE10+
  1、3、2 焦點管理
      activeElement輔助管理DOM焦點功能 
      document.activeElement(); 
      hasFocus() 確定文檔是否獲得了焦點 
      var button = document.getElementById('mybutton');
      button.focus();  //調用focus方法
      console.log(document.activeElement === button); // true 活動指向buton
      console.log(document.hasFocus()); //true 文檔獲得焦點
  1、3、3 HTMLDocument的變化
  readyState屬性
    loading;
    complete;
  是否文檔加載完成?
      if(document.readyState == 'complete'){
        //
      }
      自從IE6 區分薰染頁面是 標準 還是 混雜,檢測頁面的兼容性
        在標準模式下:document.compatMode == CSS1Compat
        在混雜模式下:document.compatMode == BackCompat
            if(document.compatMode == 'CSS1Compat'){
                alert('standards mode')
           }else{
                alert('quirks mode')
          }
          
      if (document.compatMode == “BackCompat”) {
         cWidth = document.body.clientWidth;
         cHeight = document.body.clientHeight;
         sWidth = document.body.scrollWidth;
         sHeight = document.body.scrollHeight;
         sLeft = document.body.scrollLeft;
         sTop = document.body.scrollTop;
      }
      else { //document.compatMode == “CSS1Compat”
         cWidth = document.documentElement.clientWidth;
         cHeight = document.documentElement.clientHeight;
         sWidth = document.documentElement.scrollWidth;
         sHeight = document.documentElement.scrollHeight;
         sLeft = document.documentElement.scrollLeft == 0 ? document.body.scrollLeft : document.documentElement.scrollLeft;
         sTop = document.documentElement.scrollTop == 0 ? document.body.scrollTop : document.documentElement.scrollTop;
      }
      head屬性
              document.body屬性    document.head屬性
              var head = document.head || document.getElementsByTagName('head')[0]
      1、3、4字符集屬性
        alert(document.charset)
        alert(document.defaultCharset)
      自定義字符屬性
        data-
        dataset屬性取得
        <div id='mydiv' data-name='divname'>
        var _mydiv = document.getElementById('mydiv')
        var _name = mydiv.dataset.name;
      1、3、5 插入標記
        div.innerHTML = '<script> alert('hi')<\/script>' 無效 script 無作用域
        不支持innerHTML :col colgroup frameset head html style table tbody thead tfoot tr title[ie8-]
        ie8 window.toStaticHTML方法(); 從源HTML中刪除所有腳本節點和事件處理程序;       
      1、3、6 outerHTML屬性【注意與innerHTMl區別】
        var content = document.getElementById('content') 
        alert(content.outerHTML);  // 得到整個content內容,包括content本身
        content.outerHTML = '<p>pp</p>'  //改寫整個content內容,包括content本身  ff7之前版本不支持
        insertAdjacentHTML()方法
          //content.insertAdjacentHTML(要插入位置,要插入的HTML文本)
          
        div1.insertAdjacentHTML('beforebegin','<p>beforebegin</p>') //相鄰前插入
        div1.insertAdjacentHTML('afterbegin','<p>afterbegin</p>') //第一個子元素之前插入
        div1.insertAdjacentHTML('beforeend','<p>beforeend</p>') //最後子元素之後插入
        div1.insertAdjacentHTML('afterend','<p>afterend</p>')//相信後插入
          結果:
          <p>beforebegin</p>
          <div id="div1">
              <p>afterbegin</p>
                <p>div1</p>
              <p>beforeend</p>
          </div>
          <p>afterend</p>
       1、3、6 性能:
          for(var i= 0;i<values.length;i++){
            ul.innerHTML +='<li>'+values[i]+'</li>' 
          }
          不可取
          
       1、3、7 scrollintoView() 方法
             document.getElementById('box2').scrollIntoView()
             默認scrollIntoView(true),元素頂部與窗口對齊
             scrollIntoView(false),元素底部與窗口對齊

1、DOM2 DOM3
DOM1: HTML 與 XML 文檔的底部結構
DOM2&DOM3:則在DOM1基礎上引入更多交互能力,也支持更高級的XML特性

  var supportDOM2Core0 = document.implementation.hasFeature('core','2.0')

1.1針對XML命名空間
HTML不支持XML命名空間 XHTML支持XML命名空間
<html xmlns = "http://www.w3.org/1999/xhtml";>

  <head>
  ...
</html>
XML命名空間創建前綴
   <xhtml:html xmlns = "http://www.w3.org/1999/xhtml">
  <xhtml:head>
  ...
</xhtml:html>
混合xhtml svg 
1.1.1 Node類型變化
  localName:不帶命名空間前綴的節點名稱
  namespaceURI:命名空間URI或者(未指定情況下是)null
  prefix:命名空間前綴
1.1.2 DOcument類型變化
  createELementNS
  createAttributeNS
  ...
  創建一個SVG元素
    var svg  = document.createElementNS("http://www.w3.org/2000/svg","svg");
  創建一個屬性某個命名空間的新特性
    var att = document.createAttributeNS("http://www.somewhere.com","random")
  取得所有XHTML
    var elems = document.getElementsByTagNameNS("http://www.w3.org/1999/xhtml","*")
1.1.3 Element類型變化
      getAttributeNS(namespaceURI,localName)
      getAttributeNodeNS(namespaceURI,localName)
      hasAttributeNS
      removeAttriubteNS
      setAttributeNS
      setAttributeNodeNS
 1.1.4 NamedNodeMap類型變化
    getNamedItemNS
    removeNamedItemNS
    setNamedItemNS
    
2.1 訪問元素樣式
var box = document.getElementById('box')
box.style.cssText = 'width:200px;height:100px;background-color:red'
console.log(box.style.cssText);
for (var i = 0, len = box.style.length; i < len; i++) {
  value = box.style.getPropertyValue(box.style.item(i))
  console.log(box.style.item(i) + ':' + value);
}
移除樣式:
  box.style.removeProperty('width')
2.2計算的樣式
  style對象,不包含那些從其他樣式表層疊而來並影響到當前元素的樣式信息;
    DOM2級樣式,增強document.defaultView,提供 getComputedStyle()方法
    
    方法封裝:
    function getStyle(obj,attr) {
        if(obj.currentStyle){
            return obj.currentStyle[attr];
        }
        else{
            return document.defaultView.getComputedStyle(obj,null)[attr]
        }
    }
 2.3 
  1) 元素大小
      offsetHeight :heigth border padding
      offsetWidth :width border padding
      offsetLeft  
      offsetTop
      
      獲取元素在頁面上的偏移量:offsetLeft和offsetTop與其offsetParent相加,循環至根元素;
      function getElementLeft(elem) {
        var actLeft = elem.offsetLeft;
        var current = elem.offsetParent;
        while (current !== null) {
          actLeft += current.offsetLeft;
          current = current.offsetParent
        }
        return actLeft;
      }
  2) client大小
  document.documentElement.clientWidth document.body.clientWidth
  可視區大小方法
    function getViewport() {
      if (document.compatMode == 'BackCompat') {
        return { //混雜模式
          width: document.body.clientWidth,
          height: document.body.clientWidth
        };
      } 
      else {
        return { //標準模式
          width: document.documentElement.clientWidth,
          height: document.documentElement.clientHeight
        }
      }
    }
    
  3) 包含滾動內容的元素大小,滾動大小,
      scrollHeight:無滾動條情況下,元素內容總高
      scrollWidth: 無滾動條的情況下,元素內容總寬
      scrollLeft: 滚动距离
      scrollTop: 滚动距离 
      确定文档总高度时,必须取得scrollWidth/clientWidth,serollHeight/clientHeight中的最大值,
      var docHeight = Math.max( document.documentElement.scrollHeight,document.documentElememet.clientHeight );
      var docWidth = Math.max( document.documentElement.scrollWidth,document.documentElement.clientWidht );
      IE:document.body..
      
      判断是否居于顶部:
      function scrollToTop(elem){
        if(elem.scrollTop!=0){
          elem.scrollTop = 0;
        }
      }
      obj.getBoundingClientRect()获取座標
      right - left = offsetWidth
      bottom - top = offsetHeight
   2.4遍歷 IE不支持DOM遍歷
   

IE 冒泡
Netscape Communicator事件
以 on 开头
window.onload = 中 { function s(){alert(this)} }
onclick = "s()",这种形式只能写在上面,window.onload不起作用

var inptry = document.getElementById('inp-try');
inptry.onclick = function () {

try{
    s();
}
catch (e){
    alert(123)
}

}
//兼容事件绑定及移除事件

var EventUtil = {
addHandler: function (elem,type,handle) {

  if(elem.addEventListener){
      elem.addEventListener(type,handle,false);
  }
  else if(elem.attachEvent){
      elem.attachEvent('on'+type,function(){
        handle.call(elem);
      })
  }
  else (elem['on'+type]) = handle;

},
removeHandle: function (elem, type, handle) {

  if(elem.removeEventListener){
  
      elem.removeEventListener(type,handle,false);
  }
  else if(elem.detachEvent){
      elem.detachEvent('on'+type,handle)
  }
  else (elem['on'+type]) = null;

}
}
obj.onclick = function(e){
e = e || window.evet;
alert(e.type)
}
this始终等于e.currentTarget
目标元素下:this currentTarget/target 相同
doument:currentTarget

li[0].onclick= function (e) {

  alert(e.target === this)  //true
  alert(e.currentTarget === this)  //true

}

document.onclick = function (e) {

alert(e.currentTarget===this)  //true
alert( e.target === this) //false

}