<file_sep>## 数据绑定和第一个AngularJS Web应用
- angular中数据绑定
   + 它创建实时模板来替代视图，而不是将数据合并进模板之后跟新DOM。任何一个独立视图组件中的值都是动态替换的。这个功能可以说是angular最重要的功能之一。
   + 自动数据绑定使我们可以将视图理解为模型状态的映射。当客户端的数据模型发生变化时，视图就能反应这些变化，并且不需要写任何自定义代码，它就可以工作。
   + MVC：视图-模型-控制器
   + 表现分离：视图不需要知道如何保存对象，只需要知道如何显示它。数据模型不需要同视图进行交互，只需要包含数据和操作视图的方法。控制器用来存放二者绑定在一起的业务逻辑。
   + angular会在事件循环时执行脏检查来保证数据的一致性。
   + angular使用$预定义对象，只要遇到$符号，都可以只把它看作一个angular对象。
   + $timeout本质上就是定时器
   + 数据绑定的最佳实践：由于Javascript自身的特点，以及它在传递值和引用时的不同处理方式，通常认为，在视图中通过对象的属性而非对象本身来进行引用绑定，是Angular中的最佳实践。
````js
    <div ng-controller="myCon">
        <h1>Hello {{clock.now}}</h1>
    </div>
    <script src="js/angular.js"></script>
    <script>
        var app = angular.module("myApp", []);
        app.controller("myCon", function ($scope, $timeout) {
            $scope.clock = { now: new Date() };
            var update = function () {
                $scope.clock.now =  new Date() ;
               
            };
            setInterval(function(){ //使用$timeout就不会有这样的问题。
              $scope.$apply(update) //Scope提供$apply方法传播Model的变化,setInterval产生的变化angular监控不到，和promise中的rosolve方法一样
            },1000)
            update();
        })
````
## 模块
- 在angular中，模块是定义应用的最主要方式。模块包含了主要的应用代码。一个应用可以包含多个模块，每一个模块都包含了定义具体功能的代码。
- 定义模块的好处：
    + 保存全局命名空间的清洁
    + 编写测试代码更容易，并能保持其清洁，以便更容易找到互相隔离的功能。
    + 易于在不同应用间复用代码。
    + 使应用能够以任意顺序加载代码的各个部分。
    + angular.module(name,requires);
       - name(字符串)：name是模块的名称，字符串变量。
       - requires(字符串数组)：requires包含一个字符串变量组成的列表，每个元素都是一个模块的名称，本模块依赖于这些模块，依赖需要在本模块加载之前有注入器进行预加载。
 ## 作用域
- 作用域说明 
   - 应用的作用域是和应用的数据模型关联的，同时作用域也是表达式执行的上下文。$scope对象是定义应用业务逻辑，控制器方法和视图属性的地方。
   - 作用域是应用和控制器之间的胶水。基于动态绑定，可以依赖视图在修改数据时立即更新$scope,也可以依赖$scope在其发生变化时立即重新渲染视图。
   - angular将$scope设计成和DOM类似的结构，因此$scope可以进行嵌套绑定，也就是说可以引用父类$scope的属性。
   - 将应用的业务逻辑都放在控制器中，而将相关的数据都放在控制器的作用域中，这是非常完美的结构。
- 视图和$scope的世界
   - angular启动并生成视图时，会将根ng-app元素同$rootScope进行绑定。$toorScope是所有$scope对象的最上层。
   - $scope对象在angular中充当数据模型，但与传统的数据模型不一样，$scope并不负责处理和操作数据，它只是视图和HTML之间的桥梁，它是视图和控制器之间的胶水。
 ````js
 //run方法用于初始化全局的数据，仅对全局作用域起作用。
   angular.module("myApp",[]).run(function($rootScope){
        $rootScope.name="tt";
    })
 ````  
 - angular应用的模板（HTML）中使用多种标记：   
    + 指令：将DOM元素增强为可复用的DOM组件的属性或元素。
    + 值绑定：模板语法{{}}可以将表达式绑定到视图上。
    + 表单控件：用来检查用户输入的控件。

 - 作用域能做什么
    + 提供观察者以监视数据模型的变化
    + 可以将数据模型的变化通知给整个应用，甚至是系统外的组件。
    + 可以进行嵌套，隔离业务功能和数据。
    + 给表达式提供运算时所需的执行环境。
    + 作用域包含了渲染视图时所需的功能和数据，它是所有视图的唯一源头。可以将作用域理解成视图模型。
 - $scope对象的生命周期处理有四个不同阶段
    + 创建：在创建控制器或指令时，angular会用$injector创建一个新的作用域，并在这个新建的控制器或指令运行时将作用域传递过去。
    + 链接：当angular开始运行时，所有的$scope对象都会附加或者链接到视图中。所有创建$scope对象的函数也会将自身附加到视图中。这些作用域将会注册angular应用上下文中发生变化时需要运行的函数。
       - 这些函数被称为$watch函数，angular通过这些函数获知何时启动事件循环。
    + 更新：当事件循环运行时，他通常执行在顶层$scope对象上(被称作$rootScope)，每个子作用域都执行自己的脏值检查。每个监控函数都会检查变化。如果检查到任意变化，$scope对象将会触发指定的回调函数。
    + 销毁：当一个$scope在视图不在需要时，这个作用域将会清理和销毁自己。
       - 尽管永远不会需要清理作用域（angular会处理），但是知道谁创建了这个作用域还是有用的，因为可以使用这个$scope上叫做$destory()的方法来清理这个作用域。
    + 指令和作用域：指令通常不会创建自己的$scope，但也有例外，比如ng-controller和ng-repeat(过滤器和遍历)指令会创建自己的子作用域并将它们附加到DOM元素上。
## 控制器
- 控制器在angular中的作用是增强视图，控制器是一个函数，用来向视图的作用域中添加额外的功能。
- angular同其它Javascript框架最主要的一个区别就是，控制器并不适合执行DOM操作，格式化或数据操作，以及除存储数据模型之外的状态维护操作，它只是视图和$scope之间的桥梁.
- $scope对象用来从数据模型向视图传递信息。同时，它也可以用来设置事件监听器，同应用的其它部分进行交互，以及创建与应用相关的特定业务逻辑。
- angular应用的任何一部分，无论它渲染在哪个上下文中，都有父级作用域存在。对于ng-app所处的层级来讲，它的父级作用域就是$rootScope。
   + 有一个例外：在指令内部创建的作用域被称为孤立作用域。
   + 除了孤立作用域外，所有的作用域都是通过原型继承而来，也就是说它们都可以访问父级作用域。
 ````js
 <body ng-app="myApp">
    <div ng-controller="myCon">
         <div ng-controller="myChild">
            <button ng-click="fn.hello()">点击</button>
        </div>
    </div>

     <script src="js/angular.js"></script>
    <script>
        var app = angular.module("myApp", []).run(function ($rootScope) {
            $rootScope.name = "tt";
        });
        app.controller("myCon", function ($scope) {
            $scope.person={
                name:"zzz"
            }
        })
        app.controller("myChild",function($scope){
            $scope.fn={
                hello:function(){
                    alert($scope.person.name);
                }
            }
        })
    </script>
</body>
```` 
## 表达式

- 表达式和eval(javascript)非常相似，但是由于表达式有Angular来处理，有以下特征：
     + 所有的表达式都在其所属的作用域内部执行，并有访问本地$scope的权限；
     + 如果表达式发生了TypeError和ReferenceError并不会抛出异常；
     + 不允许使用任何流程控制功能（条件控制，例如if/else）;
     + 可以接受过滤器和过滤器链
- 解析Angular表达式
     + Angular会在运行$digest循环的过程中自动解析表达式，但是手动解析表达式也是非常有用的。
     + Angular通过$parse这个内部服务来进行表达式的计算，这个服务能访问当前所处的作用域。这个过程允许我们访问定义在$scope上的原始Javascript数据和函数。
 ````js
 <body ng-app="myApp">
    <section ng-controller="oneCon">
              <input type="text" ng-model="expr" placeholder="Enter an expression">
              <h2>{{parsedValue}}</h2>
    </section>
<script src="js/angular.js"></script>
<script>
    angular.module("myApp",[]).controller("oneCon",["$scope","$parse",function($scope,$parse){
             $scope.$watch('expr',function(newVal,oldVal,scope){
                 if(newVal!=oldVal){
                     //用该表达式设置parseFun
                     var parseFun=$parse(newVal);
                     //获取经过解析后表达式的值
                     $scope.parsedValue=parseFun(scope);
                 }
             })
    }])
</script>
</body>
 ````      
 - 插值字符串
    + 插值允许基于作用域上的某个条件实时更新文本字符串。要在字符串模板中做插值插值，需要在对象中注入$interpolate服务。
    + $interpolate服务接受三个参数，其中第一个参数是必须的。
        - txt(字符串)：一个包含字符插值标记的字符串。
        - mustHaveExpression(布尔型)：如果将这个参数设置为true，当传入字符串中不含有表达式时返回null。
        - trustedContext(字符串)：angular会对已经进行过字符插值操作的字符串通过$sec.getTrusted()方法进行严格的上下文转义。
        - $interpolate服务返回一个函数，用来在特定的上下文中运算表达式。
        - 在{{previewText}}内部的文本可以将{{to}}当成一个变量来使用，并对文本的变化进行实时更新。
````js
<body ng-app="myApp">
    <section ng-controller="oneCon">
              <input type="email" ng-model="to" > //需要输入邮箱格式
              <textarea ng-model="emailBody"></textarea>
              <pre>{{previewText}}</pre>
              <p>{{to}}</p>
    </section>
<script src="js/angular.js"></script>
<script>
    angular.module("myApp",[]).controller("oneCon",["$scope","$interpolate",function($scope,$interpolate){
             $scope.$watch('emailBody',function(body){ //监控文本域输入值，如果字符串中有表达式，且去找对应的值，替换，没有则替换为空。
                if(body){
                    var template=$interpolate(body);
                    $scope.previewText=template({to:$scope.to});
                }
             })
    }])
</script>
</body>
````        
- 如果需要在文本中使用不同于{{}的符号来标识表达式开始和结束，可以在$interprolateProvider中配置
   + 用startSymbol()方法可以修改标识开始的符号，这个方法接受一个参数。
       - value(字符型):开始符号的值。
   + endSymbol结束符号的值   
   + 如果要修改这两个符号的值，需要在创建新模块时将$interpolateProvider注入进去。
````js
<body ng-app="myApp">
    <section ng-controller="oneCon">
              <input type="email" ng-model="to" >
              <textarea ng-model="emailBody"></textarea>
              <pre>__previewText__</pre>
              <p>__to__</p>
    </section>
<script src="js/angular.js"></script>
<script>
    angular.module("emaliParser",[])
    .config(['$interpolateProvider',function($interpolateProvider){
        $interpolateProvider.startSymbol('__');
        $interpolateProvider.endSymbol('__'); //用自定义__符号取代默认的{{}}符号请求插值文本。
    }])
    .factory('EmailParser',['$interpolate',function($interpolate){
        //处理解析的服务
           return {
               parse:function(txt,content){
                   var template=$interpolate(txt);
                   return template(content);
               }
           }
    }])


     angular.module("myApp",['emaliParser'])
    .controller("oneCon",["$scope","EmailParser",function($scope,EmailParser){
             $scope.$watch('emailBody',function(body){
                if(body){
                   
                    $scope.previewText=EmailParser.parse(body,{to:$scope.to});
                }
             })
    }])
</script>
</body>
````
## 过滤器
### 过滤器是用来格式化需要展示给用户的数据。
   + 在HTML中的模板绑定符号{{}}内部通过|符号来调用过滤器
       - {{name|uppercase}}
   + 在Javascript中通过$filter来调用过滤器。
       - $filter('uppercase')('Ari')
   + 以HTML的形式使用过滤器时，如果需要传递参数给过滤器，只要在过滤器名字后面加冒号即可。如果是多个参数，可以在每个参数后面都加入冒号。
       - {{123.45678|number:2}}   //取两位小数
   + 可以用|符号作为分隔符来同时使用多个过滤器。
       - currency过滤器可以将一个数据格式化为货币格式。允许设置货币符号，默认情况下会采用客户端所处区域的货币符号。
           -  {{txt|currency:'￥'}}
       - date过滤器可以将日期格式化成需要的格式，angular中内置了几种日期格式，，如果没有指定使用任何格式，默认会采用mediumDate格式。
           -  {{txt|date:'yyyy-mm-dd'}}
       - filter过滤器可以从给定数组中选择一个子集，并将其生成一个新数组返回。这个过滤器通常用来过滤需要进行展示的元素。
         + 这个过滤器的第一个参数可以是字符串，对象或是一个用来从数组中选择元素的函数。
           
            - 字符串：返回所有包含这个字符串的元素。如果想返回不包含该字符串的元素，在参数前加!符号。
            - 对象：angular将待过滤对象的属性同这个对象中的同名属性进行比较，如果属性值是字符串就会判断是否包含该字符串。如果希望对全部属性都进行对比，可以将$当作键名。
            - 对每个元素都执行这个函数，返回非假值的元素会出现在新的数组中并返回。

         + filter过滤器第二个参数，用来指定预期值同实际值进行比较的方式。
            - false 进行区分大小写的子字符串比较
            - true 用angular.equals(expected,actual)对两个值进行严格比较。
            - 函数 运行这个函数，如果是真值就接受这个元素
      + json过滤器可以将一个JSON或Javascript对象转换为字符串
     + limitTo过滤器会根据传入的参数生成一个新的数组或者字符串，新的数组或字符串的长度取决于传入的参数，通过传入参数的正负值来控制从前面还是后面开始截取。
       - {{['a','b','c']|limitTo:1}} //['a']     
     + lowercase过滤器将字符串转为小写
     + number过滤器将数字格式化成文本。第二个参数是可选的，用来控制小数点后截取的位数。
       - 如果传入了一个非数字类型，会返回空字符串。
       - {{1304375948024|number}} 1,304,375,948,024  
     + orderBy过滤器可以用表达式对指定的数组进行排序。接受两个参数，第一个是必须的，第二个可选。
       - 第一个参数类型
          + 函数：当第一个参数是函数时，该函数就会被当作排序对象的getter方法。
          + 字符串：对这个字符串进行解析的结果将决定数组的排序方向。可以传入+或-来强制进行升序或降序排列。+ 数组：在排序表达式中使用数组元素作为谓词。对于与表达式结果并不严格相等的每个元素，则使用第一个谓词。
       - 第二个参数用来控制排序的方向（是否逆向）。
       -  
       ````html
           {{
                [{'name':"zsn",'age':12},{'name':"clj",'age':10},{'name':"xpp",'age':8}]
                |orderBy:'age':'true'
            }}  
       ````
### 自定义过滤器
  + 自定义过滤器需要将它放在自己的模块中，实现一个将首字母大写的过滤器。
 ````js
     angular.module("myApp.filters",[])
    .filter('capitalize',function(){
        return function(input){
            if(input){
                return input[0].toUpperCase()+input.slice(1);
            }
        }
    })
   
     angular.module("myApp",["myApp.filters"])
    .controller("oneCon",["$scope",function($scope){
        
    }])
 ```` 
 ### 表单验证
   - angular能够将HTML5表单验证功能同它自己的验证指令结合起来使用。
      - 要使用表单验证，首先要确保表单元素有一个name属性
      - 所有输入字段都可以进行基本的验证，比如最大，最小长度等。这些功能是由新的HTML5表单属性提供的。
      - 如果想屏蔽浏览器对表单的默认验证行为，可以在表单元素上添加novalidate标记
      - input元素上使用的所有验证选项
         + 必填项： h5标记required &lt;input type="text" required />
         + 最小长度 ng-minlength="number"
         + 最大长度 ng-maxlength="number"
         + 模式匹配 使用ng-pattern="/正则表达式/"来确保输入能够匹配指定的正则表达式
         + 电子邮箱 &lt;input type="email" name="email" />
         + 数字 &lt;input type="number" name="age" />
         + url &lt;input type="url" name="homepage" />
         + 在表单中控制变量 可以使用以下格式访问这些属性
             - formName.inputFieldName.property
             - 未修改的表单--这是一个布尔属性，用来判断用户是否修改了表单。如果未修改，值为true，如果修改过值为false :formName.inputFieldName.$pristine
             - 修改过的表单--只要用户修改过表单，无论输入是否通过验证，该值都返回true：formName.inputFieldName.$dirty
             - 合法的表单--这个布尔值的属性用来判断表单的内容是否合法。如果当前表单内容时合法的，下面属性的值就是true：formName.inputFieldName.$valid
             - 不合法的表单--这个布尔属性用来判断表单的内容是否不合法。如果当前表单内容是不合法的，下面属性的值为true：formName.inputFieldName.$invalid
             - 错误--这是angular提供的另外一个非常有用的属性：$error对象。它包含当前表单的所有验证内容，以及它们是否合法的信息。formName.inputfieldName.$error;如果验证失败，这个属性的值为true；如果值为false，说明输入字段的值通过了验证
          + 一些有用的css样式
             - angular处理表单时，会根据表单当前的状态添加一些css类，这些css类的命名和前面介绍的属性很相似。
             - .ng-pristine{};.ng-dirty{};.ng-valid{};.ng-invalid{}
             - 当某个字段中的输入非法时，.ng-invlid类会被添加到这个字段上。当前列子中的站点将对应的css样式设置为：
                 + input.ng-invalid{border:1px solid red;}
                 + input.ng-valid{border:1px solid green;}
+ $parsers
    - 当用户与控制器进行交互，并且ngModelController中的$setViewValue()方法被调用时，$parsers数组中的函数会以流水线的形式被逐个调用。第一个$parse被调用后，执行结果会传给第二个$parse，以此类推。
    - 这些函数可以对输入值进行转换，或者通过$setValidity()函数设置表单的合法性。
    - 使用$parsers数组是实现自定义验证的途径之一，例如，假设我们想确保输入值在某两个数值中间，可以在$parsers数组中栈入一个新的函数，逐个函数会在验证链接中被调用。
    - 每个$parse返回的值都会被传入下一个$parser中。当不希望数据模型发生更新时返回undefined。     
````js
    angular.module("myApp",[]).directive('ontToTen',function(){
        return {
            require:"?ngModel",
            link:function(scope,ele,attrs,ngModel){
                if(!ngModel)return;
                ngModel.$parsers.unshift(
                    function(viewValue){
                        var i=parseInt(viewValue);
                        if(i>=0&&i<10){
                            ngModel.$setValidity('oneToTen',true);
                            return viewValue;
                        }else{
                            ngModel.$setValidity  ('oneToTen',false);
                            return undefined;
                        }
                    }
                )
            }
        }
    })
````

+ $formatters    
   - 当绑定的ngModel值发生了变化，并经过$parsers数组中解析器的处理后，这个值会被传递给$formatters流水线。同$parsers数组可以修改表单的合法性状态类似，$formatters中的函数也可以修改并格式化这些值。
   - 比起单纯的验证目的，这些函数更常用来处理视图中的可视变化。例如，假设我们要对某个值进行格式化。通过$formatters数组可以在这个值上执行过滤器：
        ````js
              angular.module("myApp").directive('oneToTen',function(){
              return {
                 require:"?ngModel",
                 link:function(scope,ele,attrs,ngModel){
                    if(!ngModel)return;
                    ngModel.$formatters.unshift(function(v){
                      return $filter('number')(v);
                    })
               }
           }
          })
        ````
- 组合实例   
  ---难
## 指令简介
### 指令：自定义HTML元素和属性
- 自定义指令
   + angular模块API中的.directive()方法，可以通过传入一个字符串和一个函数来注册一个新指令。其中字符串是这个指令的名称，指令名应该是驼峰命名风格的，函数应该返回一个对象。
   + directive()方法返回的对象中包含了用来定义和匹配指令所需的方法和属性。
   + 可以将自定义标签从生成的DOM中完全移除掉，并只留下有模板生成的标签。将replace设置为true可以达到这个效果。
   + 把创建的这些自定义元素称做指令(用.directive()方法创建)，因为事实上声明指令并不需要创建一个新的自定义元素。
   + 声明指令本质上是在HTML中通过元素，属性，类或注释来添加功能。
     - 合法格式： 
        - &lt;div my-directive></div>
        - &lt;div class="my-directive"></div> 
        - &lt;!--directive:my-directive-->
   + restrict属性表示angular在编译HTML时用哪种声明格式来匹配指令，可以指定一个或多个格式。
     - 元素(E),属性(A),类(C)或注释(M)  
     - 无论有多少中方式可以声明指令，推荐使用属性方式，因为它有比较好的跨浏览器兼容性。
````js
   <my-directive></my-directive>
    <script>
        angular.module("myApp", []).directive('myDirective', function () {
            return {
                restrict: 'A', //"EA"
                replace:true,
                template: '<a href="http://www.baidu.com">got0</a>',//指令模板，可以嵌套或替换

            }
        })
    </script>
````
  + 用表达式声明指令
    - 声明指令时即可以使用表达式，也可以不使用表达式
       - &lt;my-directive="someExpressin">&lt;/my-directive>
       - &lt;div my-directive="someExpressin">&lt;/div>
       - &lt;div class="my-directive:someExpressin">&lt;/div> 
       -  &lt;!--directive:my-directive someExpressin-->
  + 当前作用域
    - 由DOM通过内置指令ng-controller提供的作用域。这个指令的作用是在DOM中创建一个新的子作用域。
    - 注意，还有其它内置指令（比如ng-include和ng-view）也会创建新的子作用域，这意味着它们在被调用时行为和ng-controller类似。我们在构造自定义指令是也可以创建新的子作用域。
### 向指令中传递数据
- 在主HTML文档中，可以给指令添加myUrl和myLinkText两个属性，这两个属性会成为指令内部作用域的属性。
   - 有好几种途径可以设置指令内部作用域中属性的值。最简单的方法就是使用由所属控制器提供的已经存在的作用域。尽管简单，共享状态会导致很多其他问题。如果控制器被移除，或者控制器的作用域也定义了一个myUrl的属性，就要被迫修改代码。
   - angular允许通过创建新的子作用域或者隔离作用域来解决这个常见的问题。
   - 同之前在当前作用域中介绍的继承作用域（子作用域）不同，隔离作用域同当前DOM作用域是完全分隔开的。为了给这个新的对象设置属性，我们需要显示地通过属性传递数据，同在Javascript中给方法传递参数类似。
   - 用如下代码将指令地作用域设置成一个只包含它自己地属性地干净对象：
     - scope:{ someProperty:"needs to be set"} //行不通，不能在作用域对象内部直接设置someProperty属性。
     - 实际上创建的是隔离作用域。本质上，意味这指令有了一个属于自己的$scope对象这个对象只能在指令的方法中或者指令的模板字符串中使用。
     - 实际上要在DOM中项之前提到的那样，像给函数传递参数一样，通过属性来设置值：
        -&lt;div my-directive my-url="http://www.baidu.com" my-link-text="百度"></div>
     - 在作用域对象内部把someProperty值设置为@这个策略绑定。这个策略绑定告诉angular将DOM中some-property属性的值复制给新作用域对象中的someProperty属性。
        - scope:{someProperty:'@'}
     - 注意，默认情况下someProperty在DOM中的映射是some-property属性。如果我们想显示指定绑定的属性名，可以用如下方式：
        - sope{someProperty:'@someAttr'} //这个例子中，被绑定的属性名是some-attr而不是some-property  
     - 更进一步，还没有在DOM对应的作用域上运算表达式，并将结果传递给指令，在指令内部最终被绑定在属性上：
        - <div my-directive some-attr="{{'http://'+'www.baidu.com'}}"></div
   
````js
      <div my-directive my-url="http://www.baidu.com" my-link-text="百度"></div>
    <script>
        angular.module("myApp", []).directive('myDirective', function () {
            return {
                restrict: 'A',
                template: '<a href="{{myUrl}}">{{myLinkText}}</a>',
                replace:true,
                scope:{
                    myUrl:'@',
                    myLinkText:'@',
                }
            }
        })
    </script>
````    
- 在此之上，我们来看看如何创建一个文本输入域，并将输入值同指令隔离作用域的属性绑定起来：
   - 注意在输入标签上使用了内置指令ng-model。这个指令可以将输入的文本同$scope上的myUrl属性进行绑定。
      -  &lt;input type="text" ng-model="myUrl">&lt;div my-directive some-attr="{{myUrl}}" my-link-text="baidu"> </div>
      - 上面的代码是可以工作的，但是如果我们将文本输入字段移到指令内部并在另一个指令中进行绑定，就无法正常工作了：
      -  &lt;div my-directive some-attr="{{myUrl}}" my-link-text="baidu"> </div>
      - 本意上就是自定义指令声明了自己的独立作用域就和外部作用域隔离了 scope:{}, 
      - template:'&lt;div>&lt;input type="text" ng-model="myUrl">&lt;a href="{{myUrl}}">{{myLinkText}}</a>&lt;/div>'
      - 设置了 scope:{}后没有错误的将内部$scope属性myUrl同外部的DOM属性some-attr绑定在一起，值是通过对DOM属性进行复制被传递到隔离作用域中，难道不应该设置同名属性嘛？
      - 出现这个现象的原因是，内置指令ng-model在它自身内部的隔离作用域和DOM的作用域（由控制器提供）之间创建了一个双向数据绑定。
- 接下来在我们的隔离作用域和ng-model内部的隔离作用域之间创建一个双向数据绑定。将内部的$scope.myUrl属性同当前控制器作用域中的theirUrl属性进行绑定，在DOM中通过作用域查询来实现这个绑定。
     - 通过两个文本输入框可以方便的观察作用域是如何在Javascript中通过原型继承链接在一起的。  
     - 除了将原来的文本输入字段添加回主HTML文档外，唯一的修改使用=绑定策略代替了@。    
````js
<body ng-app="myApp">
    <label> Their URL filed:</label>
    <input type="text" ng-model="theirUrl">
    <div my-directive some-attr="theirUrl" my-link-text="baidu"> </div>

    <script>
    angular.module('myApp',[]).directive('myDirective',function(){
        return {
            restrice:'A',
            replace:true,
            scope:{
                myUrl:'=someAttr',//经过了修改。=和@符号区别在于：=是双向绑定  @符号是单向绑定
                //some-attr="theirUrl"  =拿到的是theirUrl这个对象所在的空间 @符号拿到的是theirUrl字符串
                //some-attr="{{theirUrl}}"  =拿到的是theirUrl这个对象所在的空间 @符号是单向绑定
                myLinkText:'@'
            },
            template:'<div><label> My URL filed:</label>'
            +'<input type="text" ng-model="myUrl">'
            +'<a href="{{myUrl}}">{{myLinkText}}</a></div>'
        }
    })
    
    </script>
````
## 内置指令
- 基础ng属性指令
   + ng-hreft
   + ng-src
   + ng-disabled
   + ng-checked
   + ng-readonly
   + ng-class
   + ng-style
- 布尔属性 
   + 当在angular中使用动态数据绑定时，不能简单的将这个属性值设置为true或false，因为根据标准（HTML标准）定义只有当这个属性不出现时，它的值才为false。因此angular提供了一组带有ng-前缀版本的布尔属性，通过运算表达式的值来决定在目标元素上是插入还是移除对应的属性。
   + ng-disabled可以把disabled属性绑定到以下表单输入字段上：
      - &lt;input>(text,checkbox,radio,number,url,email,submit)
      - &lt;texterea>
      - &lt;select>
      - &lt;button>
- 类布尔类型  
   + ng-href，ng-src等属性虽然不是标准的HTML布尔属性，但是由于行为相似，所以在angular源码内部和布尔属性同等对待的。
   + ng-href和ng-src都能有效帮助重构和避免错误，推荐改进代码使用。
   + ng-href：当用户点击一个由插值动态生成的链接时，如果插值尚未生效，将会跳转404，如果使用ng-href，angular会等插值生效后再执行点击链接的行为。
   + ng-src：angular会告诉浏览器再ng-src对应的表达式生效之前不要加载图像。
### 在指令中使用子作用域
- 下面将要介绍的指令会以父级作用域为原型生成子作用域。这种继承的机制可以创建一个隔离层，用来将需要协同工作的方法和数据模型对象放置在一起。
    + ng-app和ng-controller是特殊的指令，因为它们会修改嵌套在它们内部的指令的作用域。
    + ng-app为angular应用创建$rootScope，ng-controller则会以$rootScope或另一个ng-controlle的作用域为原型创建新的子作用域。
- ng-app
    + 任何具有ng-app属性的DOM元素将被标记为$rootScope的起始点。
    + $rootScope是作用域的起始点，任何嵌套在ng-app内的指令都会继承它。
    + 在Javascript代码中通过run方法来访问$rootScope
    + 不推荐实际生产中像使用全局作用域一样使用$rootScope。
    + 可以在整个文档中只使用移除ng-app。如果需要在一个页面放置多个angular应用，需要手动引导应用。
- ng-controller 
    + 内置指令ng-controller的作用是为嵌套在其中的指令创建一个子作用域，避免将所有操作和模型都定义在$rootScope上。用这个指令可以在一个DOM元素上放置控制器
    + ng-controller接受一个参数expression，这个参数是必须的。expression参数是一个angular表达式。  
    + 子$scope只是一个Javascript对象，其中含有从父级$scope中通过原型继承得到的方法和属性，包括应用$rootScope.
    + 嵌套在ng-controller中的指令有访问新子$scope的权限，但要牢记每个指令都应该遵守的和作用域相关的规则。
    + $scope对象的职责是承载DOM中指令所共享的操作和模型。
      - 操作指的是$scope上的标准Javascript方法。
      - 模型指的是$scope上保存的包含瞬间状态数据的Javascript对象。持久化状态的数据应该保存到服务中，服务的作用是处理模型的持久化。
    + 出于技术和框架方面的原因，绝对不要直接将控制器中的$scope赋值为值类型对象（字符串，布尔值或数字）。DOM中应该始终通过dian操作符来访问数据。遵守这个规范将是你远离不可预测的麻烦。
       - 控制器应该尽可以简单。虽然可以用控制器来组织所有功能，但是将业务逻辑移到服务和指令中是非常好的主意。
    + 当出现控制器的嵌套时，如果父控制器的中$scope的一个属性为值类型对象，那么由于原型继承的关系，修改子控制器$scope上的同一个属性，父控制器中的$scope属性不会改变。但如果显式的给$scope属性声明了数据类型为引用类型对象，子控制器和父控制器共用一个对象（原型继承），无论是在子控制器中还是父控制器中，修改其中的一个另一个也变化了，实现数据的同步。  
    + 注意，虽然这个特性是使用ng-controller是最重要的特性之一，但在使用任何创建子作用域的指令时，如果将指令定义中的scope属性设置为true，这个特性也会带来负面影响，下面内置指令都有同样的特性。
       - ng-include
       - ng-switch
       - ng-repeat
       - ng-view
       - ng-controller
       - ng-if 
- ng-include
    + 使用ng-include可以加载，编译并包含外部HTML片段到当前应用中，模板的url被限制在与应用文档相同的域和协议下，可以通过白名单或包装成被信任的值来突破限制。更进一步，需要考虑跨域资源共享和同源规则来确保模板可以在任何浏览器中正常加载。例如，所有浏览器都不能进行跨域的请求，部分浏览器也不能服务file://协议的资源。
       - 在开发中，可以通过命令命令行chrome --allow-file-access-from-files来禁止CORS错误。只有在紧急情况下使用这个方法，比如你的老板正站在你身后，并且所有事情都无法正常工作。
       - 在同一元素上添加onload属性可以在模板加载完成后执行一个表达式。
       - 要记住，使用ng-include时angular会自动创建一个子作用域。如果你想使用某个特定的作用域，例如controller的作用域，不会像往常一样从尾部作用域继承并创建一个新的子作用域。
       - 注意：引入文件要包裹一对单引号 <div ng-include="'myFile.htm'"></div>
- ng-switch
  + 这个指令和ng-switch-when（加子元素上）及on='propertyName'（加父元素上）一起使用，可以在properName发生变化时渲染不同指令到视图中。 
  + 注意，在switch被调用之前用ng-switch-default来输出默认值
- ng-view
  + ng-view指令用来设置将被路由管理和放置HTML中的视图的位置。
- ng-if
  + 使用ng-if指令可以完全根据表达式的值在DOM中生成或移除一个值。
  + ng-if和ng-show指令最大的区别是，它不是通过css显式隐藏DOM节点，而是真正生成或移除节点。
  + 当一个元素被ng-if从DOM中移除，同它关联的作用域也被销毁。而且当它从新加入DOM中时，会通过原型继承从它的父作用域生成一个新的作用域。
  + 同时有一个重要的细节需要知道，ngif重新创建元素时用的时它们编译后的状态。如果ng-if内部代码加载之后被jquery修改过（例如用.addClass），那么当ng-if的表达式为false时，这个DOM元素被移除，表达式再次为true时这个元素及其内部的子元素会被重新插入DOM，此时这些元素的状态会是它们的原始状态，而不是它们上次被移除时的状态。也就是说无论用jquery的.addClass添加了什么类都不会存在了。
- ng-repeat
  + ng-repeat用来遍历一个集合或为集合的每个元素生成一个模板实例。集合中的每个元素都会被赋予自己的模板和作用域。同时每个模板实例的作用域中都会暴露一些特殊的属性。
- ng-init
  + ng-init指令用来在指令被调用时设置内部作用域的初始状态。一般做小demo使用。
- {{}}
  + {{}}语法时angular内置的模板语法，它会在内部$scoep和视图之间创建绑定。基于这个绑定，只要$scope发生变化，视图就会随之自动更新。
  + 实际上它也是指令，虽然看起来不像，它是ng-bind的简略模式，用这种模式不需要创建新的元素，以此它常被用在行内文本中。
  + 注意，在屏幕可视区使用{{}}会导致页面加载时未渲染的元素发生闪烁，用ng-bind可以避免这个问题。
- ng-bind
  + HTML加载含有{{}}语法的元素后并不会立即渲染它们，导致未渲染内容闪烁（Flash of   Unrendered Content,  FOUC）。可以用ng-bind将内容和元素绑定在一起避免FOUC。内容会被当在子文本节点渲染到含有ng-bind指令的元素内。 
- ng-cloak
  + 除了使用ng-bind来避免未渲染元素闪烁，还可以在包含{{}}的元素上使用ng-cloak指令
  + ng-cloak指令会将元素内部隐藏，直到路由调用对应的页面才显式出来。
- ng-bind-template
  + 同ng-bind类似，ng-bind-template用来在视图中绑定多个表达式。
- ng-model
  + ng-model指令用来将input，select，text，area或者自定义表单控件同包含它们的作用域中的属性进行绑定。它可以提供并处理表单的验证功能，在元素上设置相关的css类（ng-valid，ng-invalid等），并负责在父表单中注册控件。
  + 它将当前作用域中运算表达式的值同给定的元素进行绑定。如果属性并不存在，它会隐式创建并将其添加到当前作用域中。
  + 我们使用ngmodel来绑定$scope上一个数据模型内的属性，而不是$scope上的属性，这样可以避免在作用域或后代作用域中发生属性覆盖。
- ng-show/ng-hide
  + ng-show和ng-hide根据所给表达式的值来显式或隐藏HTML元素。当赋值给ng-show指令的值为false时元素会被隐藏。类似的，当赋值给ng-hide指令的值为true时元素也会被隐藏。
  + 元素的显式或者隐藏是通过移除或添加ng-hide这个css类来实现的。ng-hide类被预先定义在了angular的常熟市文件中，并且它的display属性的值为none（用了!important标记）.
- ng-change
  + 这个指令会在表单输入发生变化时计算给定表达式的值。因为要处理表单输入，这个指令要和ngmodel联合使用。ngmodel中的值改变，则调用ng-change="fn()"中的函数。  
- ng-form
  + ng-form用来在一个表单内部嵌套另一个表单。普通的HTML<form>标签不允许嵌套，但是ng-form可以。
  + 这意味着内部所有的子表单合法时，外部的表单才合法。这对于用ng-reapeat动态创建的表单是非常有用的。
  + 由于不能通过字符串插值来给输入元素动态生成name属性，所以需要将ng-form指令内每组重复的输入字段都包含在一个外部表单元素内。
  + 下面css类会根据表单的验证状态自动设置：
     - 表单合法时设置ng-valid
     - 表单不合法时设置ng-invalid
     - 表单未进行修改时设置ng-pristion
     - 表单进行过修改时设置ng-dirty
  + angular不会将表单提交到服务器，除非它指定了action属性。要指定提交表单时调用哪个Javascript方法，使用下面两个指令中的一个。
    - ng-submit：在表单元素上使用。
    - ng-click：在第一个按钮或submit类型（input[type=submit]）的输入字段上使用。
    - 为了避免处理程序被多次调用，只能用上面两个指令中的一个。
````js
  <style>
        input.ng-invalid {
            border: 1px solid red;
        }

        input.ng-valid {
            border: 1px solid green;
        }
    </style>


 <body ng-app="myApp">
    <form name="signup_form" ng-controller="FormController" ng-submit="submitForm()" novalidate>
        <div ng-repeat="field in fields" ng-form="signup_form_input">
            <input type="text" name="dynamic_input" ng-required="field.isRequired" ng-model="file.name" placeholder="{{field.placeholder}}"
            />
            <div ng-show="signup_form_input.dynamic_input.$dirty&&signup_form_input.dynamic_input.$invalid">
                <span class="error" ng-show="signup_form_input.dynamic_input.$error.required">The fidld is required</span>
            </div>
        </div>
        <button type="submit" ng-disabled="signup_form.$invalid">Submit All</button>
    </form>
    <script src="js/angular.js"></script>
    <script>
        angular.module("myApp", []).controller("FormController", ["$scope", function ($scope) {
            $scope.fields = [
                { placeholder: "Username", isRequired: true },
                { placeholder: "<PASSWORD>", isRequired: true },
                { placeholder: "Email(optional)", isRequired: false },
            ];
            $scope.submitForm = function () {
                alert("it works!");
            }
        }])
    </script>
</body>

`````  

- ng-click
  + ng-click用来指定一个元素被点击时调用的方法或表达式
- ng-select
  + ng-select用来将数据同HTML的select元素进行绑定。这个指令可以和ng-model以及ng-options指令一同使用，构建精细且表现优良的动态表单。
  + ng-options的值可以时一个内涵表达式，其实中只是一个有趣的说法，简单来说就是它可以接受一个数组或对象，并对它们进行循环，将内部的内容提供给select标签内部的选项。它可以时下面两中形式。
    - 数组作为数据源
       + 用数组中的值做表；
       + 用数组中的值作为选中的标签；
       + 用数组中的值做标签组。
       + 用数组中的值作为选中的标签组。
    - 对象作为数据源
       + 用对象的键-值（key-value）做标签；
       + 用对象的键-值作为选中的标签
       + 用对象的键-值作为标签组
       + 用对象的键-值作为选中标签组  
       + for遍历子标签的内容 as遍历子标签的值 
- ng-sumbit
  + ng-submit用来将表达式同onsubmit事件进行绑定。这个指令同时会阻止默认行为（发送请求并重新加载页面），除非表单不含有action属性。
- ng-class
  + 使用ng-class动态设置元素的类，方法是绑定一个代表所有需要添加类的表达式。重复的类不会添加。当表达式发生变化，先前添加的类会被移除，新类会被添加。
- ng-attr-(suffix)
  + 当angular编译DOM时会查找花括号{{some expression}} 内的表达式。这些表达式会被自动注册到$watch服务中并更新到$digest循环中，成为它的一部分。
  + 有时浏览器会对属性进行很苛刻的限制。SVG就是一个例子
     - &lt;svg>&lt;circle cx="{{cx}}">&lt;/circle>&lt;svg>
  + 运行上面的代码会抛出一个错误，指出我们有一个非法属性。可以用ng-attr-cx来解决这个问题。注意，cx位于这个名称的尾部。在这个属性中，通过{{}}来写表达式，达到前面提到的目的。   
     - &lt;svg>&lt;circle ng-attr-cx="{{cx}}">&lt;/circle>&lt;svg>
     - 纬度和经度值转换为浮点数,这个cx属性必须是浮点数：23.993938

## 指令详解
### 指令定义
- 对于指令，可以把它简单的理解成在特定DOM元素上运行的函数，指令可以扩展这个元素的功能。
- directive()函数创建指令
  + name（字符串）指令的名称，用来在视图中引用特定的指令。
  + factory_function（函数）这个函数返回一个对象。$compile服务利用这个方法返回的对象，在DOM调用指令是来构造指令的行为。
     - 也可以返回一个函数代替对象，这个函数通常被称做链接传递函数，利用它我们可以定义指令的链接（link）功能。
   + 当angular启动引用时，它会把第一个参数当成一个字符串，并以此字符串为名来注册第二个参数返回的对象。angular编译器会解析主HTML的DOM中的元素，属性，注释和css类名中使用了这个名字的地方，并在这些地方引用对应的指令。当它找到某个已知的指令时，就会在页面中插入指令对应的DOM元素。
      - 为避免和HTML标准冲突，自定义属性最好加一个前缀。
    + 指令的工厂函数只会在编译器第一次匹配到这个指令时调用一次。和controller函数类似，通过$injetor.invoke来调用指令的函数工厂。
    + 当angular在DOM中遇到具名指令时，会去匹配已经注册过的指令，并通过名字在注册对象中查找。此时，就开始了一个指令的生命周期，指令的生命周期开始于$compile方法并结束于link方法。
+ restrice（字符串）
  - restrice是可选参数，它告诉angular这个指令在DOM中可以何种形式被声明。 

+ priority:number 优先级
  - 优先级参数可以被设置为一个数值。大多数指令会忽略这个参数，使用默认值0，但也有些场景设置高优先级是非常重要甚至是必须的。例如ngRepeat将这个参数设置为1000，这样就可以保证在同一元素上，它总是在其他指令之前被调用。
  - 如果一个元素上具有两个优先级相同的指令，声明在前面的那个会被优先调用。
  - ngRepeat是所有内置属性中优先级最高的，它总是在其他指令之前运行。这样设置主要考虑性能。
+ terminal（布尔型）
  - terminal是一个布尔型参数，可以被设置为true或false。
  - 这个参数用来告诉angular停止运行当前元素上比本指令优先级底的指令。但同当前指令优先级相同的指令还是会被执行。
  -如果元素上某个指令设置了terminal参数并具有较高的优先级，就不要再用其他底优先级的指令对其进行修饰了，因为不会调用。
  - 使用了terminal参数的例子是ngview和ngif。ngif优先级略高于ngview，如果ngif的表达式为true，ngview就可以被正常执行，但如果ngif表达式的值为false，由于ngview的优先级底就不会执行。
+ template 参数可以是模板，也可以是一个返回模板的函数。   
+ templateUrl 参数可以是一个链接，一个script模板ID，也可以是一个返回外部HTML文件路径的字符串。
### 指令作用域
- $rootScope这个特殊对象会在DOM中声明ng-app时被创建。
     + DOM每个指令调用时都可能会
        - 直接调用相同的作用域对象
        - 从当前作用域对象继承一个新的作用域对象。
        - 创建一个同当前作用域相隔离的作用域对象。

- $scope参数（布尔型或对象）
  - $scope参数为true时和ng-controller一样，都是从父级作用域继承并创建一个新的子作用域。
- 隔离作用域
  - 具有隔离作用域的指令最主要的使用场景是创建可复用的组件，组件可以在未知上下文中使用，并且可以避免污染所处的外部作用域或不经意的污染内部作用域。- 创建具有隔离作用域的指令需要将scope属性设置为一个{}对象。如果这样做了，指令的模板就无法访问外部作用域了。 

 - 绑定策略   

         
  



     




































            



       
         


<file_sep>const fs=require('fs');
const path=require('path');
const clear=require('clear');
const filename=process.argv[2];


const filearr=fs.readdirSync(path.join(process.cwd(),filename));

const arr=[];
filearr.forEach(item=>{
    fs.readFile(path.join(path.join(process.cwd(),filename),item),'utf8',(err,data,t)=>{
         if(!err){
             //arr.push(txt);
             arr[item.replace('.txt','')-1]=data;
            
         }
         if(arr.length==filearr.length){
             let num=0;
             setInterval(()=>{
                   clear();
                    console.log(arr[num++%6])
             },100)
              //console.log(arr)
         }   
    })
})<file_sep>### ajax
  - jsonp原理
````js

  
function ajax(options){
  var defaults = {
    url : '/abc',
    jsonp : 'callback'
  }
  // 覆盖默认值
  for(var key in options){
    defaults[key] = options[key];
  }

  var cbName = 'jQuery' + ('v1.11.1' + Math.random()).replace(/\D/g,'') + '_' + new Date().getTime();
  if(defaults.jsonpCallback){
    cbName = defaults.jsonpCallback;
  }

  // 给window对象添加了一个全局方法，全局方法的名称是cbName的值，而不是cbName本身
  window[cbName] = function(data){
    defaults.success(data);
  }

  // 处理业务参数 flag=1&n=123&
  var param = '';
  if(defaults.data){
    for(var key in defaults.data){
      param += key + '=' + defaults.data[key] + '&';
    }
  }
  // if(param){
  //   param = param.substring(0,param.length - 1);
  // }

  // 动态创建script标签，然后通过标签的src属性发送请求
  var script = document.createElement('script');
  script.src = defaults.url + '?' + param + defaults.jsonp + '=' + cbName + '&_=' + new Date().getTime();// jQuery413241431234143213_1231231231({"username":"lisi"})
  var head = document.getElementsByTagName('head')[0];
  head.appendChild(script);
}
````<file_sep>## node简介
+ 为什么叫Node
   - Node本质上是一个可以构建网络应用的基础框架，可以在它的基础上构建更多的东西，诸如服务器，客户端，命令行工具等。Node发展为一个强制不共享任何资源的单线程，单进程系统，包含十分适宜网络的库，为构建大型分布式应用程序提供基础设施，其目标也是成为一个构建快速，可伸缩的网络应用平台。它自身非常简单通过通信协议来组织许多Node，非常容易通过扩展来达成构建大型网络应用的目的。每一个Node进程都构成这个网络应用中的一个节点，这是它名字所含意义的真谛。
+ 单线程
   - 在Node中，JavaScript与其余线程是无法共享任何状态的。单线程的最大好处是不用像多线程编程那样处处在意状态的同步问题，这里没有死锁的存在，也没有线程上下文切换所带来的性能上的开销。
   + 弱点
     - 无法利用多核cpu
     - 错误会引起整个程序退出，应用的健壮性值得考验。
     - 大量计算占用CPU导致无法继续调用异步I/O。
   - Node中可以采用与Web Workers 相同的思路来解决大计算量的问题：child_process(子进程)
+ Node应用场景
   - I/O密集的优势主要在于Node利用事件循环的处理能力，而不是启动每一个线程为每一个请求服务，资源占用少。
   - V8的深度性能优化决定了Node在处理CPU密集型业务上的高效。主要的原因是Node的单线程，如果长时间的运算会导致CPU时间片不能释放，使得后续I/O无法发起。可适当把大型任务分解。
   + Node虽然没有提供多线程用于计算，但还是有两个方式充分利用CPU
      - Node可以通过编写c/c++扩展的方式更高效地利用CPU，将V8不能做到地性能极致的地方通过c/c++实现。
      - 如果单线程的Node不能满足需求，甚至用了c/c++扩展还觉得不够，可以通过子进程的方式，将一部分Node进程当做常驻服务进程用于计算，然后利用进程间的消息来传递结果，将计算与I/O分离，这样还能充分利用多CPU。
### 模块机制
- 工具(浏览器兼容)=》组件(功能模块)=》框架(功能模块组织)=》应用(业务模块组织)  
+ 在Node中引入模块，需要经历如下3个步骤
  - 路径分析
  - 文件定位
  - 编译执行
  - 在Node中，模块分为两类：Node提供的模块为核心模块，用户编写的模块称为文件模块。
+ 优先加载缓存
  - 浏览器缓存的是文件，Node缓存的是模块编译执行后的对象  
  - 核心模块的缓存检查优先于文件模块。
+ 路径分析和文件定位
  - 核心模块的优先级仅次于缓存加载 ，它在node的源代码编译过程中已经编译为二进制代码，其加载过程最快。
  - 路径形式的文件模块以.,..和/开始都被当成文件模块处理，require方法把路径转化为真是路径，以真实路径为索引，将编译执行的结果哦放在缓存中。
  - 自定义模块是一种它是的文件模块，可能是一个文件或者包的形式。这类模块查找最费事。
     + module.paths：模块的路径生成规则和原型链类型，从当前文件一级级向上生成对应的node_modules文件夹。查找时一级一级向上查找。
     + 不传文件扩展名的情况下，Node会.js.node.json的次序补足扩展名，依次尝试，推荐.node.json后缀文件加后缀名
  - 目录分析和包
     + require在分析扩展名后，可能没有找到对应文件，但却得到一个目录，这在引入自定义模块和逐个模块路径进行查找时经常会出现，此时node会将目录当做一个包处理。
     + 在逐个过程中，Node对CommonJS包规范进行了一定程度的支持。首先，Node在当前目录下查找package.json(CommonJS包规范定义的包描述文件)，通过JSON.parse()解析出包描述对象，从中取main属性指定的文件名进行定位。如果文件名缺乏扩展名，将会进入扩展名分析的步骤。如果main属性指定文件名错误或压根没有package.json文件，Node会将index当做默认文件名，然后依次查找index.js,index.node,index.json.
     - 如果没有找到，则自定义模块进入下一个模块路径进行查找，直到模块路径数组被遍历完，还没有找到则报错。



     <file_sep>## DOM
- DOM(文档对象模型)是针对HTML和XML文档的一个API(应用程序编程接口)。DOM描绘了一个层次化的节点树，允许开发人员添加,移除和修改页面的一部分。
- 文档节点是文档的根节点，在html中文档节点只有一个子点，为元素节点html
- NodeList对象的独特之处在于，它实际上是基于DOM结构动态执行查询的结果，因此DOM结构的变化能够自动反应在NodeList对象中。
- 由于IE8及更早版本将NodeList实现为一个COM对象，而我们不能像使用JScript对象那样使用这种对象使用数组的方法（[].slice.call(nodes,0)）把它转化为数组会导致错误，必须手动枚举所有成员。
- hasChildNodes()方法可以判断元素是否有子节点，返回布尔值。
- 每个元素都有最后一个属性ownerDocument，该属性指向表达整个文档的文档节点(document)。
- insertBefore方法接受连个参数：要插入的节点和作为参照的节点。
- replaceChild方法接受两个参数：要插入的节点和要替换的节点。
- 与使用replaceChild方法一样，通过removeChild移除的节点仍然为文档所有，但在文档中已经没有了位置。
- cloneNode方法参数为true时复制节点本身和它的子节点，为false只复制本身。
- ie9之前的版本不会为空白符创建节点。
- cloneNode方法不会复制添加到DOM节点中的JavaScript属性，例如事件处理程序等。这个方法只复制特性（在明确指定的情况下也复制子节点），其他一切都不会复制，IE在此存在一个bug，即它会复制事件处理程序，所以我们减一在复制前最好移除事件处理程序。
- normalize()方法唯一的作用是处理文本节点。当在某个节点上调用这个方法时，就会在该节点的后代节点中查找，发现空文本节点则删除；如果找到相邻的文本节点，则把它们合并为一个文本节点。
### Documnet类型
- document.referrer 获取来源页面地址
- ie7中，document.getElementById不区分大小写，并且表单元素的name属性值和要获取的id名一样，会把表单元素获取。所以name属性和id最好都是唯一的。
- 在后台，对数字索引就会调用item()，而对字符串索引就会调用namedItem()
- getElementsByTagName()标签在html中不区分大小写，但在xml中区分
- 特殊集合：
    + document.anchors，包含文档中所有带name的特性a元素。
    + document.forms，包含文档中所有的form元素
    + document.links 包含文件中所有带href特性的a元素
    + document.images包含文档中所有的img标签
- DOM一致性检查
    + hasFeature()方法接受两个参数，要检测的DOM功能的名称及版本号。如果浏览器支持给定名称和版本的功能，则该方法返回true
    + document.implementation.hasFeature("XML","1.0")   
    + 返回true有时候也不意味着实现与规范一种
- 文档写入
    + write(),writeln()后者比前者多个一个换行符(\n).都具有将输出流写入网页的功能。    
    + 如果文档加载结束后再调用document.write，那么输出的内容将会重写整个页面。
+ Element类型
    + nodetype的值为1
    + nodeName的值为元素的标签名
    + nodeValue的值为null
    + 要访问元素的标签名，可以使用nodeName属性，也可以使用tagName属性。  
    + 在html中获取的标签名始终是大写  
+ ie中createElemnet方法参数可以是一个完整的html，包括id和class等等，在ie7以下版本中document.createElemnet创建（先创建一个元素，后期添加name等属性）同一组name名称相同的标签之间没有关联性，可以通过在 createElemnet中指定完整的HTML标签来解决。    
+ ie下ul有3个子节点，其他浏览器中，ul元素都会有7个元素,包括3个li元素和4个html
````js
   <ul>
      <li></li>
      <li></li>
      <li></li>
   </ul>
```` 
### text类型
    + nodeType值为3
    + nodeName的值为"#text"
    + nodeValue的值为节点所包含的文本
    + parentNode是一个Element
    + 不支持子节点
 - document.createTextNode()创建一个文本节点   
 - 和合并文本节点相反的方法：splitText()。这个方法会将一个文本节点分成两个文本节点，参数为一个数字，原来的文本节点将包含从开始到指定位置之前的内容。
 - 可以通过nodeValue和data取到内容。
### comment 类型
 - 注释在ODM通过Comment类型来表示，nodeType为8. 
### DocumentFrament类型
 -  在所有的节点类型中，只有DocumentFragment在文档中没有对应的标记。DOM规定文档片段是一种“轻量级”的文档。可以包含和控制节点，但不会像完整的文档那样占用额外的资源。
    + node Type的值为11.
    + nodeName的值为"#document-fragment";
    + nodeValue的值为null
    + parentNode的值为null
    + 可以使用document.createDocumentFragment()创建

### 动态脚本
 - 动态脚本都是异步的
````js 
  function loadScriptString(code){
              var script=document.createElement("script");
              script.type="text/javascript";
              //style.type="text/css";
              try{
                  //js
                   script.appendChild(document.createTextNode(code));
                  
                   //IE中会报错，IE将<script>视为一个特殊的元素，不允许DOM访问其子节点
              }catch(ex){
                  //Safari 3.0之前的版本虽然不能正确的支持text代码，但却允许使用文本节点技术来指定代码
                   script.text=code;
                    //css  style.styleSheet.cssText=""
              }
              document.body.appendChild(script);
              //document.getElementByTagName("head")[0];
              //css必须追加到head中
          }
````
### 使用NodeList
  - 理解Nodelist及其"近亲"NamedNodeMap和HTMLCollection，是从整体上透彻理解DOM的关键所在。这三个集合都是”动态的“。换句话说，每当文档结构发生变化时，它们都会得到更新。
  - 尽量减少访问NodeList的次数。因为每次访问NodeList,都会运行一次基于文档的查询。
### 小结
  - DOM是语言中立的API，用于访问和操作HTML和XML文档，DOM1级将HTML和XML文档形象的看作一个层次化的节点树，可以使用Javascript来操作这个节点树，进而改变底层文档的外观和结构
   



<file_sep><?php
header('Content-type:text/html;charset=utf-8');
$images=array('2.png','3.png','1.png','3.png','2.png');
foreach($images as $image){
    $image_fh=fopen($image,'r');
    $image_data=fread($image_fh,filesize($image));
    fclose($image_fh);
    $payloads[]=base64_encode($image_data);
  
}
  $newline=chr(1);
  echo implode($newline,$payloads);
?><file_sep><title>react</title>

## react生命周期及基本用法，webpack配置

### webpack

```js
   // webpack.develop.config.js配置
    var path=require('path')
    var webpack=require('webpack')
    
    module.exports={
        entry:path.resolve(__dirname,'src/js/app.js'),
        output:{
            path:path.resolve(__dirname, 'dist'),
            filename:'bundle.js'
        },
        module:{
            rules:[
                //// es6转es5   jsx转成js
                {
                    test: /\.jsx?$/,
                    exclude: /node_modules/,
                    use: [
                        {
                            loader: "babel-loader",
                            options: {
                                // babel-preset-es2015
                                presets: ['es2015', 'react', 'stage-0', 'stage-1', 'stage-2']
                            }
                        }
                    ]
                },
                // 能够解析css的加载器
                {
                    test: /\.css$/,
                    use: ['style-loader', 'css-loader'],
                },
                // 解析sass文件的加载器
                {
                    test: /\.scss$/,
                    use: ['style-loader', 'css-loader', 'sass-loader'],
                },
                // 解析图片文件的url加载器 25000bit ~3kb
                {
                    test: /\.(png|jpg|jpeg|gif)$/,
                    use: 'url-loader?limit=25000&name=images/[name].[ext]'
                },
            ]
        },
        devtool: 'eval',
        devServer: {
            contentBase: __dirname + '/src',
            hot: true,  // �����ȸ���
            port:8080, // ָ���˿ں�
            host: 'localhost',
            open:true,    // openPage:''
        },
        plugins: [
            new webpack.HotModuleReplacementPlugin(),
        ]
    }

   //package.json配置
    "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack --config webpack.develop.config.js",
    "server": "webpack-dev-server  --config webpack.develop.config.js --content-base src",
    "publish": "webpack --config webpack.publish.config.js -p",
    "gulp": "gulp",
    "lint": "eslint --ext .js .vue src"
  },  
```
### react

- npm install react react-dom
- 下载依赖
   - npm install --global babel-cli   // 安装babel
   - npm install babel-preset-react  -dev-save// 安装babel转换jsx的包
   - npm install babel-preset-es2015 -dev-save// 安装babel转化ES6的包

- 基本配置

```js
  // index.html 入口文件
   <div id="app"></div>
   <script src="bundle.js"></script>
  //js>app.js  配置的js入口
   import React,{Component}from 'react'
   import ReactDOM from 'react-dom'

   import Hello from '../components/Hello'

    ReactDOM.render(
        <div>
            <Hello/>
        </div>,
        document.getElementById('app')
    )
    //components》.js 一个组件
    import React,{Component}from 'react'

    // import './hello.css'
    
    export default class Hello extends Component{
        render(){
            return (
                <div>
                    <h1>
                        你好世纪
                    </h1>
                </div>
            )
        }
    }
```

#### 组件的生命周期分成三个状态：
 - Mounting：已插入真实 DOM
 - Updating：正在被重新渲染
 - Unmounting：已移出真实 DOM

- 生命周期

![](./img/QQ图片20170814212015.png)

#### 代码使用(在ES6中用ES5的写法会报错)

- ES5写法
```js
  var Hello = React.createClass({
    getInitialState() {
        return { liked: false };
    },
    render: function() {
        console.log(this.state.liked);
        return(
            <div>
                <h1 style={style}>Hello world</h1>
                <br/>
                <image/>
            </div>
        )
    }
  });
  module.exports=Hello;
```
- ES6写法
```js
  export default class Hello extends Component {
    constructor(props) {
        super(props);

        this.state = { count: 'es6'};
    }
    render() {
        return (
            <div>
                <h1 style={style}>Hello world{this.state.count}</h1>
                <br/>
                <image/>
            </div>
        )
    }
  }
```

- 属性扩散：标签上的自定义属性可以封装成一个对象在标签上使用： {...props}
- 小括号按照html解析，大括号按照js解析
- react-devtools 谷歌浏览器插件，使react的调试工具，可以监视react中的变量
- react在销毁之前调用的方法unmount，释放一些资源，释放一些权限，移除事件监听
- 和state属性进行绑定的标签为受控组件




## 
- react ant.design 蚂蚁金服的组件库
- golp
- 极客学院
 >  http://react-guide.github.io/react-router-cn/docs/guides/advanced/ComponentLifecycle.html


 - react native 江青青 李华明

<file_sep>- UA(navigator.userAgent)嗅探技术

## 种子模块

- 种子模块应该包含如下功能：对象扩展，数组化，类型绑定，简单的事件与卸载，无冲突处理，模块加载和domReady。<file_sep>/**
 * Created by yoyo on 2017-05-05.
 */



function getPageX(e) {
  //先检测是否存在pageX
  if (e.pageX) {
    return e.pageX;
  } else {
    //pageX的值是可视区域横坐标+页面左侧的卷曲距离
    return e.clientX + myScroll().left;
  }
}

function getPageY(e) {
  //先检测是否存在pageX
  if (e.pageY) {
    return e.pageY;
  } else {
    //pageX的值是可视区域横坐标+页面左侧的卷曲距离
    return e.clientY + myScroll().top;
  }
}


/**
 * 兼容事件对象
 * @param e 获取事件对象
 * @returns {*|Event} 返回兼容后的事件对象
 */
function getEvent(e) {
  return e || window.event;
}


/**
 * 自己实现多次给同一标签添加事件不覆盖的函数
 * @param tag 要添加事件的标签
 * @param eventName 事件类型名 - 不需要加 on
 * @param fn 事件处理程序
 */
function myAddEvent(tag, eventName, fn) {
  var oldEvent = tag["on" + eventName];
  if (typeof oldEvent == "function") {
    tag["on" + eventName] = function () {
      oldEvent();
      fn();
    };
  } else {
    tag["on" + eventName] = fn;
  }
}


/**
 *
 * @param tag
 * @param attr
 * @returns {*}
 */
function getStyle(tag, attr) {
  if (tag.currentStyle) {
    return tag.currentStyle[attr];
  } else {
    return getComputedStyle(tag, null)[attr];
  }
//        return tag.currentStyle?tag.currentStyle[attr]:getComputedStyle(tag,null)[attr];
}

/**
 * 获取元素的内部文本
 * @param tag
 * @returns {*}
 */
function getText(tag) {
  if (typeof tag.innerText != "undefined") {
    return tag.innerText;
  } else {
    return tag.textContent;
  }
}

/**
 * 设置元素内部的文本
 * @param tag
 * @param text
 */
function setText(tag, text) {
  if (typeof tag.innerText != "undefined") {
    tag.innerText = text;
  } else {
    tag.textContent = text;
  }
}


/**
 *
 * @param clsName
 * @param tarEle 想要在某个标签内进行查找 (可选)
 * @returns {*}
 */
function getByClass(clsName, tarEle) {
  tarEle = tarEle || document.body;
  if (typeof document.getElementsByClassName == "function") {
    return tarEle.getElementsByClassName(clsName);
  } else {
    var allTag = tarEle.getElementsByTagName("*");
    var resultArr = [];//保存最终获取结果
    
    for (var i = 0; i < allTag.length; i++) {
      var tempClass = allTag[i].className;
      var tempArr = tempClass.split(" ");
      
      for (var j = 0; j < tempArr.length; j++) {
        if (tempArr[j] == clsName) {
          resultArr[resultArr.length] = allTag[i];
          break;//减少循环的执行次数
        }
      }
    }
    return resultArr;
    
  }
}

/**
 * 获取当前节点的所有兄弟
 * @param me
 * @returns {Array}
 */
function getSb(me) {
  var meDie = me.parentNode;
  var resultArr = [];
  var xiongdis = meDie.children;
  for (var i = 0; i < xiongdis.length; i++) {
    if (xiongdis[i] != me) {
      resultArr.push(xiongdis[i]);
    }
  }
  return resultArr;
  
}

/**
 * 获取后一个兄弟节点
 * @param node  元素节点
 * @returns {*|Node}  返回获取到的元素节点 或者null
 */
function getNextEleSib(node) {
  var ns = node.nextSibling;
  while (null != ns && 1 != ns.nodeType) {
    ns = ns.nextSibling;
  }
  return ns;
}

/**
 * 获取前一个兄弟节点
 * @param node  元素节点
 * @returns {*|Node}  返回获取到的元素节点 或者null
 */
function getPreEleSib(node) {
  //获取node的前一个兄弟节点
  var ps = node.previousSibling;
  while (null != ps && 1 != ps.nodeType) {
    ps = ps.previousSibling;
  }
  return ps;
}

function myScroll() {
  return {
    scrollTop: window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0,
    scrollLeft: window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0
  };
}

function myClient() {
  return {
    height: window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight || 0,
    width: window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth || 0
  };
}<file_sep>const fs = require('fs');
const path = require('path');
const mark = require('marked');

const filepath = process.argv[2];
const css = fs.readFileSync(path.join(__dirname, './github.css'), 'utf8')
let mdpath = path.resolve(process.cwd(), filepath)
fs.readFile(mdpath, 'utf8', (err, data) => {
  const arr=data.match(/<title>([\u4e00-\u9fa5a-zA-Z0-9]{0,20})<\/title>/);
  const title=arr?arr[1]:"note";
  const mk= mark(data.replace(/<title>([\u4e00-\u9fa5a-zA-Z0-9]{0,20})<\/title>/,""),'gbk');
  // console.log(mk);
  const html = `<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>${title}</title>
    <style>
    markdown-body {
      padding:20px;
    }
    ${css}</style>
</head>
<body>
<section class='markdown-body'>
 ${mk}
 </section>
</body>
</html>`

  fs.writeFile(mdpath.replace('.md', '.html'), html, (err) => {

  })
})<file_sep>### defer和asysc异同
  - 都是给js脚本加的属性，能起到异步加载脚本的效果（anysc是ES6才提出的）
  - defer异步加载，在加载完后要等整个页面加载完成才执行。async也是异步加载，但加载完就会直接执行。
 ### documnet.documentMode和documnet.compatMode 
  - documnet.compatMode表示浏览器处于什么模式：如果是标准模式，则document.compatMode的值等于”CSS1Compat“，如果是怪异模式，则document.compatMode的值等于”BackCompat“。
  - documnet.documentMode ie8+才有，IE8有3中不同的呈现模式，而这个属性正是为了区分这些模式。这个属性的值如果是5，则表示怪异模式（即IE5模式）；如果是7，则表示IE7仿真模式；如果是8，则表示IE8标准模式。
 ### css content计数器(ie8+)
   - counter-reset:就是“计数器-重置”的意思。其实就是“班级命名”，主要作用就是给计数器起个名字,顺便告诉下从哪个数字开始计数。默认是0.
   - counter-increment:就是“计数器-递增”的意思。值为counter-reset的1个或多个关键字。后面可以跟随数字，表示每次计数的变化值。如果缺省，则使用默认变化值1 .普照源(counter-reset)唯一，每普照(counter-increment)1次，普照源增加1次计数值。
   - counter(name, style)只是输出而已 counters(name, string, style)
### 浅析Web开发中前端路由实现的几种方式
    - 这两个Api都会操作浏览器的历史栈，而不会引起页面的刷新。不同的是，pushState会增加一条新的历史记录，replaceState则会替换当前的历史记录。所需的参数相同，在将新的历史记录存入栈后，会把传入的data（即state对象）同时存入，以便以后调用。同时，这俩api都会更新或者覆盖当前浏览器的title和url为对应传入的参数。
    - 要注意，这两个api都是不能跨域的
````js
  /**
*parameters
*@data {object} state对象，这是一个javascript对象，一般是JSON格式的对象字面量。
*@title {string} 可以理解为document.title，在这里是作为新页面传入参数的。
*@url {string} 增加或改变的记录，对应的url，可以是相对路径或者绝对路径，
*url的具体格式可以自定。
*/
history.pushState(data, title, url) //向浏览器历史栈中增加一条记录。
history.replaceState(data, title, url) //替换历史栈中的当前记录。
var currentState = history.state;  //如果没有则为null。
````   
 <file_sep>## html5元素Canvas
- Canvas本质上是一块画布，通过js动态绘制图像。
  + 可以绘制简单的2D图像。
  + 使用WebGL绘制3D图像。

- 基本用法
  + Canvas是双标签，标签中的文本内容是在浏览器不支持该标签是出现的，一定要在标签上设置宽高;
  + 取得绘图上下文对象的的引用：元素.getContent("2d"),获取2D上下文,在使用前要判断浏览器是否支持getContent;
  + toDataURL()方法可以导出画布中的图片，只有一个参数，即图片的MIME（默认png）类型格式。但是如果绘制到画布上的图像来自不同的域，该方法会抛异常。
```js
 var drawimg=document.querySelector(".drawing");
  if(drawimg.getContext)
    {   
           //var context=drawimg.getContext("2d");
           var imgURI=drawimg.toDataURL("image/png");
           var image=document.createElement("img");
           image.src=imgURI;
           document.body.appendChild(image);
    }
```
 ### 2D上下文
 - 2D上下文可以简单绘制2D图像，比如矩,弧形和路径。原点左边为画布左上角，x向右为正，y向下为正。
   + 填充（context.fillStyle="颜色"）和描边（context.strokeStyle="颜色"）,所有涉及到描边和填充的操作都使用这两个样式，直到被重置。
   + 填充矩形：context.fillRect();描边矩形：context.strokeRect();透明矩形（用来删除某一块图型）:context.clearRect();四个参数(不带用单位)，单位都为像素：x轴，y轴，宽度，高度;
   + 描边线条宽度context.lineWidth="10";lineCap控制线条末端是平头，圆头还是方头（"butt","round"或"square"）;lineJoin属性控制线条相交是圆交，斜交，还是斜接（'round','bevel'或'miter'）。
   + 注意：一定要先设置描述，一旦设置了画图方式，后面的描述就不会在影响当前图片了。
   ```js
       var context=drawimg.getContext("2d");
       context.lineCap="round";
       context.strokeStyle="green";
       context.strokeRect(11,111,51,1);

   ```
- 绘制路径
  + 要绘制路径，首先要调用beginPath()方法。表示要开始绘制新路径。
 
  + arc(x,y,radius,startAngle,endAngle,counterclockwise):以（x,y）为原型绘制一条弧线，弧线半径为radius，起始和结束角度分别为startAngle和endAngle。最后一个参数表示startAngle和endAngle是否按照逆时针方向计算，值为false表示按照顺时针方向计算。
  + arcTo(x1,y1,x2,y2,radius):从上一点开始绘制一条曲线，到(x,y)为止，并且以给定的半径radius穿过(x1,y1).
  + bezierCurveTo(c1x,c1y,c2x,c2y,x,y):从上一点开始绘制一条曲线，到（x,y）为止，并且以(c1x,c1y)和(c2x,c2y)为控制点。
  + lineTo(x,y):从上一点开始绘制一条直线，到(x,y)为止。
  + moveTo(x,y):将绘图游标移动到(x,y),不画线。
  + quadraticCurveTo(cx,cy,x,y):从上一点开始绘制一条二次曲线，到(x,y)为止，并且以(cx,xy)作为控制点。
  + rect(x,y,width,height):从点(x,y)开始绘制一个矩形，宽度和高度分别由width和height指定。这个方法绘制的是矩形路径，而不是strokeRect()和fillRect()所绘制的独立的形状。

   + 创建完路径后，如果像绘制一条连接到路径起点的线条，可以调用closePath()。如果路径已经完成，可以用fillStyle填充，可以调用fill()方法。另外，stroke()方法对路径描边，描边使用strokeStyle。最后还可以调用clip(),这个方法可以在路径上创建一个截取区域。
   + 不带数字的钟表盘
     ```js
       var drawing=document.querySelector("canvas");
          //      确定浏览器支持<canvas>元素
      if(drawing.getContext)
      {
          var context=drawing.getContext("2d");
          //          开始路径
          context.beginPath();
        //          绘制外圆
          context.arc(100,100,99,0,2*Math.PI,false);
        //          绘制内圆
          context.moveTo(194,100);
          context.arc(100,100,94,0,2*Math.PI,false);
       //          绘制分针
          context.moveTo(100,100);
          context.lineTo(100,15);
      //          绘制时针
          context.moveTo(100,100);
          context.lineTo(35,100);
      //          描边路径
          context.stroke();
      }      


     ```
  + isPointInPath(x,y)判断路径被关闭之前画布上的某一个点是否位于路径上。







<file_sep>## 基本概念
- 本章内容
  - 语法
  - 数据类型
  - 流控制语句
  - 理解函数
### 语法
- 区分大小写
  + ECMAScript中的一切（变量,函数名和操作符）都区分大小写。
- 标识符 
  - 所谓的标识符，就是指变量，函数，属性的名字，或函数的参数。标识符可以按照下列格式规则组合起来的一个或多个字符：
     + 第一个字符必须是一个字母，下划线(_)或者一个美元符号($);
     + 其他字母可以是字母，下划线，美元符号或数字。
     + 按照惯例，ECMAScript标识符采用驼峰命名法。
- 注释
     + 单行注释：//
     + 多行注释 /* */       
- 严格模式
     + ECMAScipr5引入了严格模式的概念 。
     + 要在整个脚本开启严格模式，可以在顶部添加这行代码:function(){ "use strict" // 函数体}  
     + 支持严格模式的浏览器包括IE10+和其他浏览器
        

<file_sep>## 精华
  - 给函数原型添加方便注册属性的方法
  Function.prototype.method=function(name,func){
      this.prototype[name]=func;
      return this;
  }
  - JavaScript有两种注释符：/**/和//，使用前者，如果在正则表达式中出现了*/，这对注释代码是不安全的，推荐使用后者。
## 对象
  - delete操作符只能删除对象本身的属性，不能删除原型链上的属性。  
  - 把全局性的资源都纳入到一个命名空间下，可以减少全局变量污染。
## 函数
  -  函数用于代码复用，信息隐藏，组合调用，所谓编程，就是将一组需求分解成一组函数与数据结构的技能。
### 函数对象
  - 每个函数对象创建时，会附加两个隐藏输出：函数上下文和实现函数行为的代码（JavaScript在创建函数对象时，会给一个“调用”属性，当当用函数时，可以了解为调用函数的调用属性）。
  - 通过函数字面量创建的函数对象包含一个连到外部上下文的连接，这被称为闭包
### 调用
  -   在JavaScript中一共有四种调用模式：方法调用模式，函数调用模式，构造器调用模式和apply调用模式。这些模式在初始化关键参数this存在差异。
  - 方法调用模式：当一个函数被保持为对象的一个属性时，称为它的一个方法。通过this可取到它们所属对象的上下文的方法称为公共方法。
  - 函数调用模式:当一个函数并非一个对象的属性时，那么它就是当成一个函数调用的。以这种模式调用函数，this被绑定到全局对象，这个是语言上设计的一个错误，应该绑定到外部函数的this变量。
  - 构造器调用模式：JavaScript是一门基于原型继承的语言，但当今大多数语言都是基于类的语言，所以它提供了一套和基于类的语言类似对象构建语法。
  - Apply函数调用：因为JavaScript是一门函数式的面向对象编程语言，所以函数可以拥有方法。Apply允许我们选择this的值。
### 递归
  - 通过递归获取dom元素
 ````js
      var walk_the_DOM=function walk(node,func){
        //对当前dom对象的自定义处理方案  
        func(node);
        //获取当前元素的第一个子元素
        node=node.firstChild;
        //判断当前元素有子元素则循环，判断当前元素的下一个是否是元素
        while(node){
            //递归当前元素
            walk(node,func);
            //获取下一个元素
            node=node.nextSibling;
        }
    }
                               //attr自定义属性名，value自定义属性值
    var getElementsByAttribute=function(att,value){
        var result=[];
        //获取页面有某种自定义属性的标签
        walk_the_DOM(document.body,function(node){
            //判断是否是元素节点，是则返回自定义属性值
            var actual=node.nodeType===1&&node.getAttribute(att);
            //如果当前元素有att自定义属性，则返回自定义属性的属性值
            if(typeof actual==="string"&&(actual===value||typeof value
            !=='string')){
                result.push(node);
            }
        })
        return result;
    }
 ```` 
 ### 闭包
   - 避免在循环中创建函数，可以带来无谓的计算，还容易引起混淆，可以在循环外创建一个辅助函数。

 ````js
  var add_the_handles = function (nodes) {
            function dj(i) {
                return function () {
                    console.log(i);
                }

            }
            for (var i = 0; i < nodes.length; i++) {
                nodes[i].onclick = dj(i);
            }
        }
 ````
 ### 模块 
   - 模块是一个提供接口却隐藏状态与实现的函数或对象。
   - 模块模式的一般形式是，一个定义了私有变量和函数的函数，利用闭包创建可以访问私有变量和函数的特权函数；最后返回这个特权函数，或者把它们保存到一个可以访问的地方。
   - 一个替换字符串HTML字符实体替换为对应的字符。
   ````js

         Function.prototype.method = function (name, func) {
             //判断是否已经有该方法
            if (!this.prototype[name]) {
                this.prototype[name] = func;
                return this;
            }

        }

        String.method('deentityify', function () {
            //字符实体只要初始化一次，且只能在当前函数内部访问（闭包）
            var entity = {
                quot: '"',
                lt: "<",
                gt: ">"
            }
            return function () {
                                   //()给字符串进行分组
                return this.replace(/&([^&;]+);/g, function (a, b) {
                    var r = entity[b];
                    return typeof r === "string" ? r : a;
                })
            }
        }());

        console.log("&lt;&quot;&gt;".deentityify());
   ````
   ### 级联
    - 有一些方法没有返回值，让这些方法返回this而不是undefined就可以启用级联。
   ### 函数柯里化
    - 柯里化，也常译为“局部套用” ,是把多参数函数转化为一系列单参数并进行调用的技术。
        + 柯里化三个常见的作用：1.参数复用；2.提前返回（在判断浏览器(类型)事件绑定的时候，只判断一次）；3.延时计算（bind）。
````js 
        Function.method('curry',function(){
        var slice=Array.prototype.slice,
        args=slice.apply(arguments),
        that=this;
        return function(){
            return that.apply(null,args.concat(slice.apply(arguments)));
        }
    })
````
### 记忆
   - 本质是就是把计算结果保存在一个数组中，防止重复计算同样的值。
```` js
     var memoizer=function(memo,formula){
        var recur=function(n,m){
            var result=memo[n];
            if(typeof result!=='number'){
                result=formula(recur,n);
                memo[n]=result;
            }
            return result;
        };
        return recur;
    }
    var factorial=memoizer([1,1],function(recur,n){
          return n*recur(n-1);
    })
    console.log(factorial(5))
```` 
## 继承    
### 函数化
  - 函数化模式有很大的灵活性。它相比伪类模式不仅带来的工作更少，还让我们得到更好的封装和信息隐藏，以及访问父类方法的能力。函数化模式的本质就是函数中传递的对象经过每个函数都在增强。
````js
      var mammal=function(spac){
        var that={};
        that.get_name=function(){
            return spac.name;
        }
        that.says=function(){
            return spac.saying||'';
        }
        return that;
    }
    var myMammal=mammal({name:'herb'});
    var cat=function(spec){
        spec.saying=spec.saying||'meow';
        var that=mammal(spec);
        that.purr=function(n){
            var i,s='';
            for(var i=0;i<n;i+=1){
                if(s){
                    s+='-';
                }
                s+='r';
            }
            return s;
        }
        that.get_name=function(){
            return that.says()+' '+spec.name+' '+that.says();
        }
        return that;
    }
   var myCat=cat({name:'Henrietta'});

   Object.method('superior',function(name){
       var that=this,
       //获取父类的同名方法
       method=that[name];
       return function(){
           //返回一个同功能的新方法
           return method.apply(that,arguments);
       }
   })
  var coolCat=function(spac){
      var that=cat(spac),
      //返回一个和父类方法功能一样的新方法。
      super_get_name=that.superior('get_name');
      that.get_name=function(n){
          //在子类的基础上扩展父类的方法。
          return 'like '+super_get_name()+' baby';
      }
      return that;
  }
 var myCoolCat=coolCat({name:'Bix'});
 var name=myCoolCat.get_name();
 console.log(name);
````  
### 部件
  - 可以构造一个给任何对象添加简单事件处理特性的函数，它会给对象添加一个on方法，一个fire方法和一个私有的事件注册对象。
  - 用这种方法，一个构造器可以从一套部件中把对象组装起来。
````js
  var eventuality=function(that){
    var registry={};
    that.fire=function(event){
        //在一个对象上触发一个事件，该事件可以是一个包含事件名称的字符串，
        //或者是一个拥有包含事件名称的type属性的对象
        //通过对on方法注册的事件处理对象中匹配事件名称相同的函数进行调用
        var array,func,handler,i,type=typeof event==="sting"?event:event.type;
        //如果事件对象中有这个事件，则把事件对应的函数进行循环调用。
        if(registry.hasOwnProparty(type)){
            array=registry[type];
            for(var i=0;i<array.length;i+=1){
                handler=array[i];
                func=handler.method;
                //如果该方法是一个字符串形式的名字，那么寻找到该函数。
                //如果注册事件对应的函数是自身的方法，method参数可以是一个字符串
                if(typeof func==='string'){
                    func=this[func];
                }
                func.apply(this,handler.parameters||[event]);
            }
        }
        return this;
    }
    //type:事件类型 method：方法：parameters方法参数
     that.on=function(type,method,parameters){
         var handler={
             method:method,
             parameters:parameters
         };
         //判断当前对象是否已注册该事件
         if(registry.hasOwnProparty(type)){
             registry[type].push(handler);
         }else{
             //没有则添加一个新数组，数组有一个值，为当前事件调用的函数。
             registry[type]=[handler];
         }
         return this;
     }
     return that;

}
 eventuality(that);
````
## 正则表达式
   - 可以用到正则的方法有：regexp.exec,regexp.test,string.match(返回指定的值和值的位置),string.replace,string.search(indexof是更底层的方法，虽然不能使用正则，但效率更高),string.split
   - (?:...)?表示一个可选的非捕获型分组。通常用非捕获型分组来替代少量不优美的捕获型分组是很好的方法，因为捕获会有性能上的损失。
   - \1表示指向(分组1)所捕获到文本的一个引用，所以它能再次匹配。
### 正则表达式分组
     - 捕获型：一个捕获型分组是一个包围在圆括号中的正则表达式分支。任何匹配这个分组的字符都会被捕获。每个捕获分组都被指定了一个数字。在正则表达式中第1个捕获的分组是1，第二个捕获的分组是2，以此类推。
     - 非捕获型：非捕获型分组有一个(?:前缀。非捕获型分组仅做简单的匹配，并不会捕获所匹配的文本。这回带来微弱的性能优势。非捕获型分组不会干扰捕获型分组的编号。
     - 向前正向匹配：向前正向匹配分组有一个(?=前缀。它类似于非捕获型分组，但在这个组匹配后，文本会倒回到它最开始的地方，实际上并不匹配任何东西。这不上一个好的特性/
     - 向前负向匹配：向前负向匹配分组有一个(?!前缀。它类似于向前正向匹配分组，但只有当它匹配失败时它才继续向前进行匹配。这不是一个好的特性。
## 方法     
### 数组
   - concat方法参数一个新数组，包含一份array的浅复制并把一个或多个item附加到其后。如果参数item是一个数组，那么它的每个元素会被分别添加（数组中就它一个会对加入的参数是数组时进行展开）。
   - pop方法移除array的最后一个元素并返回该元素。
   - push方法把一个或多个参数item附加到一个数组的尾部。和concat方法不同，它会修改array，如果参数item是一个数组，它会把参数数组作为单个元素添加到数组中，并返回数组新长度。
   - reverse把数组反转，并返回本身。
   - shift方法移除数组array中第一个元素并返回该数组shift通常比pop慢的多。
   - slice(start,end)方法对array中的一段代码浅复制。
   - splice(start,deleteCount,item)方法从array中移除一个或多个元素，并用新的item替换它们，返回一个包含被移除元素的数组。
   - unshift方法和push一样，把元素添加到数组中。但是添加到开始而不是尾部。
 ### function
 ````js
    Function.method("bint",function(that){
                           //[1]本质上就是把1传递给slice，把arguments除了第一个外的成员转化为数组。
            var method=this,args=[].slice.apply(arguments,[1]);
            return function(){
                method.apply(that,[].slice.apply(arguments).concat(args));
            }

        })

        var zhi=function(){
            console.log(this.value);
        }.bint({value:"tt"});
        zhi();
 ````
### String
     - string.charAt(pos): charAt方法返回在string中pos位置处出现的字符，Javascript没有字符类型，返回的时一个字符串。如果pos小于0或大于等于字符串长度，它会返回空字符串。
     - string.charCodeAt(pos):以整数形式返回pos位置处字符的字符码位。如果小于0或者大于等于字符串长度返回NaN。
     - string.concat():把字符串连接在一起。
     - sting.indexOf(searchString,position):查找字符串searchString，有则返回字符串所在位置，没有则返回-1.position设置查找的起始位置。
     - string.lastindexOf:从后往前找。
     - string.localeCompare(that):比较两个字符串，如果string比字符串that小，那么结果为负数。如果相等则为0.
     - string.match(regexp):match方法可以让字符串和一个正则表达式进行匹配。它依据g标识来决定如何进行匹配，如果没有g标识，那么调用string.match(regexp)的结果和regexp.exec(string)的结果一样。如果regexp带有g标识，那么它生成一个包含所有匹配(除捕获分组之外)的数组。
     - string.replace第一个参数可以是参数或字符串，第二个参数可以是捕获组或者字符串或者函数。

     ````js
        //把匹配到的字符串替换成这个字符串中的第一个捕获分组
        var oldareacode=/\((\d{3})\)/g;var p='(555)666-1212'.replace(oldareacode,'$1-');
     ````
     - string.search(regexp)和正则test方法类似，不支持全局g匹配。返回是匹配到字符串出现的位置。
     - string.slice(start,end):复制string的一部分构造一个新字符串。
     - string.split(separator,limit):separator可以是一个字符串或者正则表达式，可选参数limit可以限制被分割的片段数量（取前几片）。
     - string.substring(start,end):用法和slice方法一样，但是它不能处理负数参数(参数小于0当成0处理)，没有理由去使用它，请使用slice（参数小于0反向取反）。
     - str.substr(start,len):第一个参数小于0，从后向前处理。
     - string.toLowerCase():大写转小写
     - string.toUpperCase():小写转大写
     - string.fromCharCode(char..):工具一串数字编码返回一个字符串。
````js
       var a=String.fromCharCode(67,97,116);
````   

## 附录E
 ###JSON语法
   - JSON有六种类型：对象，数组，数字，字符串，布尔值，和特殊值null。
   - 一个JSON解析器 自定义把JSON格式字符串解析为对象的方法。
````js
    var json_parse = function () {
            //这是一个能把JSON文本解析成JavaScript数据结构的函数。
            //它是一个简单的递归降序解析器

            //在另一个函数中定义此函数，以避免创建全局变量

            var at, //当前字符索引
                ch, //当前字符
                escapee = {    
                    '"': '"',
                    '\\': '\\',
                    '/': '/',
                    b: 'b',
                    f: "\f",
                    n: "\n",
                    r: "\r",
                    t: '\t'
                },
                text,
                error = function (m) { //当某处出错时，调用error
                    throw {
                        name: 'SyntaxError',
                        message: m,
                        at: at,
                        text: text
                    };
                },
                next = function (c) {
                    //如果提供了参数c，那么检验它是否匹配当前字符。
                    if (c && c != ch) {
                        error("Expected '" + c + "' instead of '" + ch + "'");
                    }
                    //获取下一个字符。当没有下一个字符时，返回一个空字符串。
                    ch = text.charAt(at);
                    at += 1;
                    return ch;
                },  //解析一个数字值
                number = function () {
                    var number, string = '';
                    if (ch === '-') {
                        string = '-';
                        next('-');
                    }
                    while (ch >= '0' && ch <= '9') {
                        string += ch;
                        next();
                    }
                    if (ch === '.') {
                        string += '.';
                        while (next() && ch >= '0' && ch <= '9') {
                            string += ch;
                        }
                    }
                    if (ch == 'e' || ch === 'E') {
                        string += ch;
                        next();
                        if (ch === '-' || cch === "+") {
                            string += ch;
                            next();
                        }
                        while (ch >= '0' && ch <= '9') {
                            string += ch;
                            next();
                        }
                    }
                    number = +string;
                    if (isNaN(number)) {
                        error("Bad number");
                    } else {
                        return number;
                    }
                },//解析一个字符串值
                string = function () {
                    var hex,
                        i,
                        string = '',
                        uffff;
                        //当解析字符串时，必须找到“和\字符
                    if (ch === '"') {
                        while (next()) {
                            if (ch === '"') {
                                next();
                                return string;
                            } else if (ch === '\\') {
                                next();
                                if (ch === 'u') {
                                    uffff = 0;
                                    for (i = 0; i < 4; i += 1) {
                                        hex = parseInt(next(), 16);
                                        if (!isFinite(hex)) {
                                            break;
                                        }
                                        uffff = uffff * 16 + hex;
                                    }
                                    string += String.fromCharCode(uffff);
                                } else if (typeof escapee[ch] === "string") {
                                    string += escapee[ch];
                                } else {
                                    break;
                                }
                            } else {
                                string += ch;
                            }
                        }
                    }
                    error("Bad string");
                }, //跳过空白
                white = function () {
                    while (ch && ch <= ' ') {
                        next();
                    }
                },
                //
                word = function () {
                    switch (ch) {
                        case 't':
                            next('t');
                            next('r');
                            next('u');
                            next('e');
                            return true;
                        case 'f':
                            next('f');
                            next('a');
                            next('l');
                            next('s');
                            next('e');
                            return false;
                        case 'n':
                            next('n');
                            next('u');
                            next('l');
                            next('l');
                            return null;     
                    }
                    error("Unexpected '" + ch + "'");
                },
                value, //值函数的占位符
                //解析一个数组值
                array = function () {
                    var array = [];
                    if (ch === '[') {
                        next('[');
                        white();
                        if (ch === ']') {
                            next(']');
                            return array;
                        }
                        while (ch) {
                            array.push(value());
                            white();
                            if (ch === ']') {
                                next(']');
                                return array;
                            }
                            next(',');
                            white();
                        }
                    }
                    error("Bad array");
                },
                //解析一个对象值
                object = function () {
                    var key,
                        object = {};
                    if (ch === '{') {
                        next('{');
                        white();
                        if (ch === '}') {
                            next('}');
                            return object;
                        }
                        while (ch) {
                            key = string();
                            white();
                            next(':');
                            object[key] = value();
                            white();
                            if (ch === '}') {
                                next('}');
                                return object;
                            }
                            next(',');
                            white();
                        }
                    }
                    error("Bad object");
                };
            //解析一个JSON值。它可以是对象，数组，字符串，数字或一个词。
            value = function () {
                white();
                switch (ch) {
                    case '{':
                        return object();
                    case '[':
                        return array();
                    case '"':
                        return string();
                    case '-':
                        return number();
                    default:
                        return ch >= '0' && ch <= '9' ? number() : word();
                }
            };
            //返回json_parse函数。它能访问上述所有的函数和变量
            return function (source, reviver) {
                var result;
                text = source;
                at = 0;
                ch = ' ';
                result = value();
                white();
                if (ch) {
                    error("Syntax error");
                }
                //如果存在reviver函数，就递归对这个新结构调用walk函数，
                //开始时先创建一个临时的启动对象，并以一个空字符串作为键名保存结果，
                //然后传递每个‘名/值’对给reviver函数去处理可能存在的转化
                //如果没有reviver函数，就简单返回这个结果。
                return typeof reviver === 'function' ?
                    function walk(holder, key) {
                        var k, v, value = holder[key];
                        if (value && typeof value === 'object') {
                            for (k in value) {
                                if (Object.hasOwnProperty.call(value, k)) {
                                    v = walk(value, k);
                                    if (v !== undefined) {
                                        value[k] = v;
                                    } else {
                                        delete value[k];
                                    }
                                }
                            }
                        }
                        return reviver.call(holder, key, value);
                    }({ '': result }, '') : result;
            }
        }();
````
     


   <file_sep>
const fs=require('fs');
const path=require('path');
const iconv=require('iconv-lite');

const msname=process.argv[2];
// console.dir(iconv)
// console.log(process.mainModule)

const musicstr=iconv.decode(fs.readFileSync(path.join(process.cwd(),msname)),'gbk');

const msArr=musicstr.split('\n');
msArr.forEach((item)=>{
   let strArr=/\[(\d{2})\:(\d{2})\.(\d{2})\]\s(.+)/.exec(item);
   
   if(strArr)
    {
        setTimeout(function() {
           console.log(strArr[4])
       }, parseInt(strArr[1])*60*1000+parseInt(strArr[2])*1000+parseInt(strArr[3]));
    }else{
        console.log(item);
    }
//    console.log(strArr);
})
// console.log(msArr);<file_sep>## Loading and Execution 加载和运行

### Grouping Scripts 成组脚本

  - 由于每个script标签下载时阻塞页面解析过程，所以限制页面script总数也可以改善性能。这个规则对内联脚本和外部脚本同样适用。每当页面解析碰到一个script标签时，紧接着有一段事件用于代码执行。最小化这些延时事件可以改善页面整体性能。
  - 每个HTTP请求都会产生额外的性能负担，下载一个100kb的文件比下载四个25kb的文件要快。
  - 考虑后台实现联合句柄的方式，javascript脚本src带多个参数为js文件名，后台返回一个多文件合成的打包文件。
  

### 动态创建脚本

  -  动态创建脚本的重点在于，无论何处启动下载，文件的下载和运行都不会阻塞页面其它的处理过程。
     + 当文件使用动态脚本节点下载时，返回的代码通常立即执行（除了FIrefox和Opera，他们将等待此前所有的动态脚本节点执行完毕），当脚本不依赖其它脚本则没有问题，但如果依赖其它脚本则可以出现问题。
     + Firefox,Opera,Chorem和Safari都会在script节点接收完成之后发出一个load事件。
     + Internet Explorer支持另一种实现，它发出一个readystatechange事件，script元素有一个readyState属性，它的值随着下载外部文件的过程而改变。readyState有五种取值：uninitialized-默认状态，loading-下载完成，interactive-下载完成但尚不可用,complate-所有数据已经准备好。
     + 但ie有时出现loading，有时只出现complate，最可靠的办法时这两种状态都进行检查。
     + 如果是多重依赖，可用使用嵌套调用。多个文件次序十分重要，可用把多个文件打包成一个，因为是异步的，使用一个大文件并没有什么损失。
     + 支持跨域
  ````js
     function loadScript(url, callBack) {
            var script = document.createElement("script");
            script.type = "text/javascript";
            if (script.readyState) { //IE
                script.onreadystatechange = function () {
                    if (script.readyState == "loaded" || script.readyState == "complete") {
                        //    释放监听script脚本加载状态改变的事件,保证事件不会被触发两次。
                        script.onreadystatechange = null;
                        callBack();
                    }
                }
            } else {
                script.onload = function () {
                    callBack();
                }
            }
            script.src=url;
            document.getElementsByTagName("head")[0].appendChild(script);
        }
  ````
### XHR脚本注入

   - 另一个非阻塞的方式获得脚本的方法是使用XMLHttpRequest(XHR)对象将脚本注入到页面中。
   - 这种方法的主要优点是，你可用下载不立即执行的JavaScript代码。由于代码返回在script标签之外（换句话说不受script标签约束）,它下载后不会自动执行，这使得你可用推迟执行，直到一切都准备好了，另一个优点是，同样的代码在所有现代浏览器中都不会引发异常。
   - 此方法最主要的限制是：javascript文件必须与页面放置在同一个域内。因为这个原因，大型网页通常不采用xhr脚本注入技术。

## Data Access 数据访问

### 四种基本的数据存储位置

  - Literal values-直接量： 直接量仅仅代表直接，而不存储于特定位置。javascript的直接量包括：字符串，数字，布尔值，对象，数组，函数，正则表达式，具有特殊含义的空值，以及未定义。
  - Variables 变量：开发人员使用var关键字创建用于存储数据值。
  - Array items-数组项：具有数字索引，存储一个javascript数组对象。
  - Object members-对象：具有字符串索引，存储一个javascript对象。

### 标识符识别性能

  - 总的趋势是，对所有浏览器来说，一个标识符所在的位置越深，读写它的速度越慢。采用优化的JavaScript引擎的浏览器，如Safari4，访问域外标识符时没有这种性能损失。
  - 尽可能的使用局部变量：用局部变量存储本地范围之外的变量值，如果题目在函数中的使用多于一次。如document对象。
  - 无论是with表达式还是try-catch表达式的catch子句，以及函数中包含()的函数，都被认为是动态作用域。一个动态作用域只因代码运行而存在，无法通过静态解析。
     + 优化的Javascript引擎，企图通过分析代码来确定哪些变量应该在任意时刻被访问，来加快标识符识别过程。这些引擎企图避开传统的作用域查找，取代以标识符方式进行快速查找。但涉及到一个动态作用域后，这个优化就不起作用了，引擎需要切回慢速的基于哈希表的标识符识别方法，更像传统的作用域搜索。
   - Closures,Scope,and Memory 闭包，作用域，和内存
     + 一个函数的激活对象于运行期上下文一同销毁。当涉及闭包时，激活对象就无法销毁了，因为引用仍然存在于闭包的[[Scope]]属性中。这意味着脚本中闭包与非闭包相比，需要更多内存开销。
   - 缓存对象成员的值，在处理嵌套对象成员时特别重要。  
   - 总结：
      + 局部变量比域外变量快，因为它位于作用域链的第一个对象中。变量在作用域总位置越深，访问所需时长越长。全局变量总数最慢的，因为它们总数位于作用域链的最后一环。
      + 嵌套对象成员会造成重大性能影响，尽量少用。
      + 一个属性或方法在原型中的位置越深，访问它的速度越慢。
      + 可用将经常使用的对象才成员，数组项和域外变量存入局部变量中。然后，访问局部变量的速度会快于那些原始变量。

## DOM Scripting DOM 编程

  - 使用innerHTML和DOM方法创建一个1000行的表，在ie6中，innerHTML比对手快3倍，但在最新的基于WebKit的浏览器中慢于对手。
  - 只表示元素节点的DOM属性（html标签）和表示所有节点的属性。
````js
   Property                 Use as a replacementfor
   children                 childNodes
   childElementCount        childNodes.length
   firstElementChild        firstChild
   lastElementChild         lastChild
   nextElementSibing        nextSibling
   previousElementSibling   previousSibling     
   
   //IE7,8,9只支持children，其它不支持？？其它浏览器都支持
````
   - querySelectorAll返回一个NodeList--由符合条件构成的类数组对象。这个选择器API在IE8开始兼容，当需要联合查询时，性能比getElementsByTagName快了2-6倍。
   - getElementsByTagName返回的时一个HTML集合。

### 重排和重绘

   - 一颗DOM树，表示页面结构，一颗渲染树，表示节点如何展示。  
   - 获取布局信息的操作将导致刷新队列动作，意味着使用了以下方法
      + 布局信息由这些属性和方法返回最新的数据，所以浏览器不得不运行渲染队列中待改变的项目并重新排版以返回正确的值。
      + 在改变风格过程中，最好不要使用这些查询API。任何一个访问都将刷新渲染队列。即使你正在获取那些最近未发生改变的或者与最新改变无关的布局信息。
````js
    offsetTop序列  scrollTop序列  clienTop序列  getComputedStyle()(currentStyle in IE);
````    

### 最小化重绘和重排版    

  - 改变风格：3次改变元素的风格，在老版浏览器中重绘（重排）了3次，大多数现代浏览器优化了只进行一次重排。
    + 更高效的方法是使用cssText，合并在一起执行，只修改一次DOM。
    + 另一个一次性修改风格的办法是修改css的类名称。

### 批量修改DOM

  - 当需要对DOM元素进行多次修改时，可以通过以下步骤减少重排和重绘的次数。
     + 1.从文档流中摘除元素。
     + 2.对其进行多次改变。
     + 3.将元素带回文档中
     + 有三种方法可以把元素从文档中摘除
          - 隐藏元素，进行修改，然后显示它。
          - 使用一个文档片段在已存DOM之外创建一个子树，然后将它拷贝到文档中(document.createDocumentFragment)。
          - 将原始元素拷贝到一个脱离文档的节点中，修改副本，然后覆盖原始元素。
          - 推荐尽可能的使用文档片段（第二张解决方案），因为它涉及到最少数量的DOM操作和重排版。

### 缓冲布局信息

   - 浏览器通过队列化修改和批量运行的方法，尽量减少重排次数。当查询布局信息如偏移量，滚动条位置，或风格属性时，浏览器刷新队列并执行所有修改操作，以返回最新的数值。最好是减少对布局信息的查询次数，查询时将它赋值给局部变量，并用局部变量来计算。

### 将元素提出动画流

   - 使用以下步骤可以避免大部分页面进行重排版
       + 1.使用绝对坐标定位页面动画的元素，使它位于页面布局流之外。
       + 2.启动元素动画。当它扩大时，它临时覆盖部分页面。这是一个重绘过程，但只影响页面的一小部分，避免重新排版并重绘一大块页面。
       + 3.动画结束时，重新定位，从而只一次下移文档其它元素的位置。

#### IE和:hover

   - 如果大量元素使用了:hover会降低反应速度。 

### 总结

   - DOM访问和操作时现代网页应用中很重要的一部分，但每次通过桥梁从ECMAScript岛到达DOM岛时，都会被收取“过桥费”。为减少DOM编程中性能的损失，请牢记以下几点：
       + 最小化DOM访问，在javaScript端做尽可能多的事情。
       + 在反复访问的地方使用局部变量存放DOM引用。
       + 小心地处理HTML集合，因为他们表现出“存在性”，总是对底层文档重新查询。将集合的length属性缓存到一个变量中，在迭代中使用这个变量。如果经常操作这个集合，可以把集合拷贝到数组中。
       + 可能的话，使用速度更快的API，诸如querySelectorAll和firstElementChild。
       + 注意重绘和重排版；批量修改风格，离线操作DOM树，缓存并减少对布局信息的访问。
       + 动画中使用绝对坐标，使用拖放代理。
       + 使用事件托管技术最小化事件句柄数量。

## 算法和流程控制总结

   - for,while,do-while循环的性能特性相似，谁也不比谁更快或更慢。
   - 除非要迭代遍历一个属性未知的对象，否则不要使用for-in循环。
   - 改善循环性能的最好办法是减少每次迭代中的运算量，并减少循环迭代次数。
      + 如先取集合的长度记录下来。
      + 对循环进行倒序，(for var i=10;i--){}判断i是否为true。
    - 一般来说，switch总数比if-else更快，但并不总是最好的解决方法。
    - 当判断条件较多时，查表法（数组查询）比if-else或者switch更快。
    - 浏览器的调用栈尺寸限制了递归算法在javascript中的应用；栈溢出错误导致其它代码也不能正常执行。
    - 如果遇到一个栈溢出错误，将方法修改为一个迭代算法或者使用制表法（把计算结果缓存到一个数组中，下次遇到同样的计算直接返回）可以避免重复工作。
    - 运行的代码总量越大，使用这些策略带来的性能提示就越明显。

## 字符串和正则表达式

   - 加和加等于操作
      + 发生了四个步骤： str+="one"+"two";
          - 1.内存中创建了一个临时字符串
          - 2.临时字符串的值被赋予“onetwo”
          - 3.临时字符串与str的值进行连接
          - 4.结果赋予str
          - str+=“one”;str+="two";可以通过两个离散表达式直接将内容附加在str上避免了临时字符串。在大多数浏览器上这样做可加快10%-40%;
          - str=str+"one"+"two";这就避免了使用临时字符串，因为赋值表达式开头以str为基础，一次追加一个字符，从左往右依次连接。如果改变连接顺序，就会失去这种优化。这与浏览器合并字符串时分配内存的方法有关。除IE以外，浏览器尝试扩展表达式左侧字符串的内存，然后简单的将第二个字符串拷贝到它的尾部。如果在一个循环中，基本字符串位于最左端，就可以避免多次复制一个越来越大的基本字符串。
          - 这些技术并不适用与IE。它们几乎没有任何作用，在IE8上甚至比IE7和早期版本更慢。这与IE执行的连接操作的机制有关。在IE8中，连接字符串只是记录下构成新字符串的各部分字符串的引用。在最后时刻（当你真正使用连接后的字符串时），各部分字符串才被逐个拷贝到一个新的“真正的”字符串中，然后用它取代先前的字符串引用，所以并非每次使用字符串时都发生合并操作。
   - 数组连接（join）
     + 在大多数浏览器上，数组连接比连接字符串的其它方法更慢，但在事实上，为一种补偿方法，在IE7或者更早的浏览器上它是连接大量字符串唯一高效的途径。
   - concat
     + 追加一个或多个字符串，大多数情况下比简单的+和+=慢一些， 看起来和数组连接差不多，但通常它更慢（Opera除外），而且它还潜伏着灾难性的性能委托，正如IE7和更早版本中使用+和+=创建大字符串一样。

### 正则表达式优化 

  - 分支：当一个正则表达式扫描目标字符串时，它从左往右逐个扫描正则表达式的组成部分，在每个位置上测试能不能找到一个匹配。对于每一个量词和分支，都必须决定如何继续进行。如果是一个量词（诸如*,+?,或者{2,}）,正则表达式都必须决定何时匹配更多的字符；如果遇到分支(|),它必须从这些选项中选择一个进行尝试。
  - 回溯：每当正则表达式做出这样的决定，如果有必要的话，它会记住另一个选项，以备将来返回后使用。如果所选的方案匹配成功，正则表达式将继续扫描正则表达式模板，如果其全部匹配也成功了，那么匹配就结束了。但是如果选择的方案未能发现相应匹配，或者后来的 匹配也失败了，正则表达式将回溯到最后一个决策点，然后在剩下的选项中选一个。它继续这样下去，直到找到一个匹配，或者量词和分支选项的所有可能的排列组合都尝试失败了，那么它将放弃这一过程，然后移动到此过程开始位置的下一个字符上，重复此过程。
  - *贪婪匹配和*？惰性（又名非贪婪）量词
  - 回溯失控（无意义的回溯）的解决方案：
     + 具体化：用更具体的替代过于宽泛的。
     + 使用前瞻和向后引用列举原子组:(?=(pattern to make atomic))\1
         - 在任何打算使用原子组的模式中这个结构都是可重用的。只要记住，你需要适当的后向引用次数，如果你的正则表达式包含多个捕获组。
         - 正则表达式每次找到一个中间标签就退出一个前瞻，它在前瞻过程中丢弃所有回溯位置，下一个后向引用简单地重新匹配前瞻过程中发现地字符，将他们作为实际匹配地一部分。
  - 提高正则表达式效率的更多方法
      + 关注如何让匹配更快失败
      + 正则表达式以简单的，必须的子元开始
      + 尽量避免一个正则表达式做太多的工作。复杂的搜索问题需要条件逻辑，拆分为两个或多个正则表达式让容易解决，通常也更高效，每个正则表达式只在最后的匹配结果中执行查找。在一个模板中完成所有工作的正则表达式怪兽很难维护，而且容易引起回溯相关的问题。
   - 正则表达式去字符串手尾空格。
 ````js
  if(!String.prototype.trim){
      String.prototype.trim=function(){
          return this.replace(/^\s+/,"").replace(/\s+$/,"");
      }
  }
 ````   

## 响应接口  

  - 总的来说，大多数浏览器有一个单独的处理进程，它由两个任务所共享：JavaScript任务和用户界面更新任务。每个时刻只有其中一个的操作得以执行，也就是说当JavaScript代码运行时用户界面不能对输入产生反应，反之亦然。或者说，当JavaScript运行时，用户界面就被“锁定”了。管理号JavaScript运行时间对网页应用的性能很重要。
  - JavaScript和UI更新共享的进程通常被称作浏览器UI线程（虽然对所有浏览器来说“线程”一词不一定准确）。此UI线程围绕这一个简单的队列系统工作。任务被保存在队列中直至进程空闲。一旦空闲，队列中的下一个任务将被检索和运行。这些任务不是运行JavaScript代码，就是执行UI更新，包括重绘和重排。此进程中最令人感兴趣的部分是每次输入均导致一个或多个任务被加入队列。
  - 当所有的UI线程任务执行之后，线程进入空闲状态，并等待更多任务被添加到队列中。空闲状态是理想的，因为所有的用户操作都会立即引发一次UI更新。如果用户企图在任务运行时与页面交互，不仅没有及时的UI更新，而且不会有新的UI更新任务被创建和加入队列。实际上，大多数浏览器在JavaScript运行时停止UI线程队列中的任务，也就是说JavaScript任务必须尽快结束，以免对用户体验照成不良体验。
  - 浏览器限制
     + 浏览器在JavaScript运行时间上采取了限制。此类限制有两个：调用栈尺寸限制和长时间脚本限制。
  - 分解任务
     + 因为浏览器定时器精度问题，最好设置延时时间大于25毫秒
     + 通常将一个任务分解成一系列子任务。如果一个函数运行时间太长，那么查看它是否可以分解成一系列能够短时间完成的较小的函数。可以将一行代码简单的看作一个原子任务，多行代码组合在一起构成一个独立任务。某些函数可以基于函数调用进行拆分。
     + 如果函数运行时间太长，它可以拆分成一系列更小的步骤，把独立方法放在定时器中调用。可以将每个函数放入一个数组。
````js
         function saveTask(arr) {
            var newArr = arr.concat();
            setTimeout(function () {

                var fn= newArr.shift();
                fn();
                if (newArr.length > 0) {
                   saveTask(newArr);

                }
            }, 25)
        }
````   
 - 限时运行代码 
   + 可以使用批处理代码。
   + JavaScript可连续运行的最大时间时100毫秒，最好是不要让任何JavaScript代码持续运行超过50毫秒。
   + 推荐使用定时器序列，同一时间只有一个定时器存在，只有当这个定时器结束时才创建一个新的定时器。以这种方式使用定时器不会带来性能问题。
   + 要在网页应用中限制高频率重复定时器的数量。建议创建一个独立的重复定时器，每次执行多个操作。

### 网页工人线程--必须在服务器环境下执行

 - 自JavaScript诞生以来，还没有办法在浏览器UI线程之外运行代码。网页工人线程API改变了这种状况，它引入一个接口，使代码运行而不占用浏览器UI线程的时间。作为最初的HTML5的一部分，工人线程API已经分离出去成为独立的规范。
     + 网页工人线程对网页应用来说是一个潜在的巨大性能提升，因为新的工人线程在自己的线程中运行JavaScript。这意味着，工人线程中的代码运行不仅不会影响浏览器UI，而且也不会影响其他工人线程中运行的代码。
 - 工人线程运行的环境
    + 由于网页工人线程不绑定UI线程，这也意味着它们将不能访问许多浏览器资源。JavaScript和UI更新共享一个进程的部分原因是它们之间互访频繁，如果这些任务失控将导致糟糕的用户体验。网页工人线程修改DOM将导致用户界面出错，但每个网页工人线程都有自己的全局运行环境，只有JavaScript特性的一个子集可用。工人线程的运行环境由下列部分组成：
       - 一个浏览器对象，只包含四个属性：appName,appVersion,userAgent和platform    
       - 一个self对象指向全局工人线程对象
       - 一个importScripts()方法，使工人线程可以加载外部JavaScript文件。
       - 所有的ECMASript对象，诸如OBject，Array，Data，等等
       - XMLHttpRequest 构造器
       - setTimeout和setInterval方法
       - close方法可立即停止工人线程
    + 因为网页工人线程有不同的全局运行环境，不能在JaavaScript代码中创建。事实上，需要创建一个完全独立的JavaScript文件，包含那些在工人线程中运行的代码。要创建网页工人线程，必须传入这个JavaScript文件的URL：
       -  var worker=new Worker("code.js")
       -  此代码一旦执行，将为指定文件创建一个新线程和一个新的工人线程运行环境。此文件被异步下载，直到下载并运行完之后才启动工人线程。
- 工人线程交互
   + 工人线程和网页代码通过事件接口进行交互。网页代码可通过postMessage()方法向工人线程传递数据，它接受单个参数，即传递给工人线程的数据。此外，在工人线程中还有onmessage事件句柄用于接受信息。
````js
 var worker=new Worker("code.js");
 worker.onmessage=function(event){
     alert(event.data);
 }
 worker.postMessage("Nicholas");
````    
  + 工人线程从message事件中接收数据。这里定义了一个onmessage事件句柄，事件对象具有一个data属性存放传入的数据。工人线程可通过它自己的postMessage()方法将信息返回给页面
````js
// inside code.js
 self.onmessage=function(event){
     self.postMessage("Hello," + event.data+"!")
 }
````   
 - 最终的字符串结束于工人线程的onmessage事件句柄。消息系统是页面和工人线程之间唯一的交互途径。  
   + 只有某些类型的数据可以使用postMessage()传递。可以传递原始值（string,number,boolean,null,和undefined）,也可以传递Object和Array的实例，其他类型就不允许了。有效数据被序列化，传入或传出工人线程，然后反序列化。即使看上去对象直接穿了过去，实例其实是同一个数据完全独立的表述。视图传递一个不支持的数据类型将导致JavaScript报错。
- 加载外部文件
  + 当工人线程通过importScript()方法加载外部的JavaScript文件，它接收一个或多个URL参数，指出要加载的JavaScript文件网址。工人线程以阻塞方式调用importScript()，直到所有文件加载完成并执行之后，脚本才继续执行。由于工人线程在UI线程之外运行，这种阻塞不会影响UI响应。例如：
```js
   //inside code.js
   importScripts("file1.js","file2.js");
   self.onmessage=function(event){
       self.postMessage("Hello, "+event.data+"!")
   }
   //此代码第一行包含两个JavaScript文件，它们将在工人线程中使用
```        

#### 实际用途
  + 网页工人线程适合于那些纯数据的，或者于浏览器无关的长运行脚本。它看起来用处不大，而网页应用程序中通常有一些数据处理功能受益于工人线程，而不是定时器。
  + 考虑这样一个例子，解析一个很大的JSON字符串。假设数据足够大，至少需要500毫秒才能完成解析任务。很显然时间太长了以至于不能允许JavaScript在客户端运行它，因为它会干扰用户体验。此任务难以分解成用于定时器的小段任务，所以工人线程成为理想的解决方案。下面的代码说明了它在网页上的应用。

````js
   var worker=new worker("jsonparser.js")
   worker.onmessage=function(event){
       var jsonData=event.data;
       evaluateData(jsonData);
   }
   worker.psotMessage(jsonText);


   //inside of jsonparser.js

   self.onmessage=function(event){

       var jsonText=event.data;

       var jsonData=JSON.parse(jsonText);

       self.postMessage(jsonData);
   }
```` 
 - 请注意，即使JSON.parse()可能需要500毫秒或更多时间，也没有必要添加跟多代码来分解处理过程。此处理过程发生在一个独立的线程中，所以可以让它一直运行解析过程而不会干扰用户体验。
 - 页面使用postMessage()将一个JSON字符串传给工人线程。工人线程在它的onmessage时间句柄中收到这个字符串也就是event.data，然后开始解析它。完成时所产生的JSON对象通过工人线程的postMessage()方法传回页面。然后此对象便成为页面onmessge事件句柄的event.data。请记住，此工程只能在FIrefox3.5和更高版本中运行，而Safari4和Chrome3中，页面和工人线程之间只允许传递字符串。
     + 解析一个大字符串只是许多受益于网页工人线程的任务之一。其它可能受益的任务如下：
        - 编/解码一个大字符串
        - 复杂数学运算（包括图像和视频处理）
        - 给一个大数组排序

### 总结

- JavaScript和用户界面更新在同一个进行内运行，同一时刻只有其中一个可以运行。着意味着当JavaScript代码正在运行时，用户界面不能响应输入，反之亦然。有效的管理UI线程就是要确保JavaScript不能运行太长时间，以免影响用户体验。最后，请牢记如下几点：
   + Javascript运行时间不应该超过100毫秒。过长的运行时间导致UI更新出现可察觉的延迟，从而对整个用户体验产生负面影响。
   + JavaScript运行期间，浏览器响应用户交互行为存在差异。无论如何，JavaScript长时间运行将导致用户体验混乱和脱节。
   + 定时器可用于安排代码推迟执行，它使得你可以将长运行脚本分解成为一系列较小的任务。
   + 网页工人线程是新式浏览器才支持的特性，它运行你在UI线程之外运行JavaScript代码而避免锁定UI。
   + 网页应用程序越复杂，积极主动的管理UI线程就越显的重要。没有什么JavaScript代码可以重要到允许影响用户体验的程度。

## Ajax异步JavaScript和XML

### 请求数据

- 有五种常用技术用于向服务器请求数据
  + XMLHttpRequest(XHR)
  + Dynamic script tab insertion 动态脚本标签插入
  + iframes
  + Comet
  + Multipart XHR 多部分XHR
  + 在现代高性能JavaScript中使用的三种技术是XHR，动态脚本标签插入和多部分XHR使用Comet和iframe(作为数据传输技术)往往是极限情况。
- XMLHttpRequest
  + readyState对于4表示整个响应报文已经接收完成可用于操作。
  + readyState等于3则表示此时正在与服务器交互，响应报文还在传输之中。着就是所谓的“流”，它是提高数据请求性能的强大工具。
````js
   var xhr=new XHRHttpRequest();
   xhr.onreadystatechange=function(){
       if(xhr.readystate===3){
           var data=xhr.responseText;
       }
       if(xhr.readystate===4){
            var responseHander=xhr.getAllResponseHeaders();
            var data=xhr.responseText;
       }
   }
   xhr.open('get',url);
   xhr.setRequestHander("X-Requested-With","XMLHttpRequest");
   xhr.send();
````  
- 动态脚本标签插入
   + 该技术克服了XHR的最大限制：它可以从不同域的服务器上获取数据。这是一种黑客技术，而不是实例化一个专用对象，你用JavaScript创建了一个新脚本标签，并将它的源属性设置为一个指向不同域的URL。
   + 但是动态脚本标签插入与XHR相比只提供更少的控制。不能通过请求发送信息头。参数只能通过GET方法传递，不能用POST。你不能设置请求超时或重试，实际上，你不需要知道它是否失败了。你必须等待所有数据返回之后才可以访问它们。不能访问响应信息头或者像访问字符串那样访问整个响应报文。
   + 最后一点非常重要。因为响应报文被用作脚本标签的源码，它必须是可以执行的JavaScript。你不能使用裸XML，或者裸JSON，任何数据，无论什么格式，必须在一个回调函数之中被组装起来。
   + 尽管有这些限制，此技术仍然非常迅速。其响应结果是运行JavaScript，，而不是作为字符串必须被进一步处理。正因为如此，它可以是客户端上获取并解析数据最快的方法。
   + 请小心使用这种技术从你不能直接控制的服务器请求数据。JavaScript没有权限和访问控制的概念，所以在你的页面上使用动态脚本标签插入的代码都可以完全控制整个页面。包括修改任何内容，将用户重定向到另一个站点，或跟踪他们在页面上的操作并将数据发送给第三方。使用外部来源的代码时务必非常小心。
- 多部分XHR   
  + 这里介绍最新的技术，多部分XHR(MXHR)允许你只用一个HTTP请求就可以从服务端获取多个资源。它通过将资源（可以是css文件，HTML片段，JavaScript代码，或base64编码的图片）打包成一个由特定分隔符界定的大字符串，从服务器发送到客户端。JavaScript代码处理此长字符串，根据它的媒体类型和其它“信息头”解析每个资源
  + 这段PHP代码读取三个图片，并将它们转化成base64字符串。它们之间用一个简单的字符，UNICODE的1，链接起来，然后返回给客户端。
  - 图像不是从base64转化成二进制，而是使用data:URL并指定image/jpeg媒体类型。

````js
   var req=new XMLHttpRequest();
        req.open('GET','mxhr.php');
        req.onreadystatechange=function(){
            if(req.readyState==4){
                splitImages(req.responseText);
            }
        }
        req.send(null);

        function splitImages(imageString){
            var imageData=imageString.split("\u0001");
            var imageElement;
            for(var i=0,len=imageData.length;i<len;i++)
            {
                 imageElement=document.createElement("img");
                 imageElement.src='data:image/jpg;base64,'+imageData[i];
                 document.getElementById('container').appendChild(imageElement);
            }
        }
````  
        
````php
 <?php
header('Content-type:text/html;charset=utf-8');
$images=array('2.png','3.png','1.png','3.png','2.png');
foreach($images as $image){
    $image_fh=fopen($image,'r');
    $image_data=fread($image_fh,filesize($image));
    fclose($image_fh);
    $payloads[]=base64_encode($image_data);
  
}
  $newline=chr(1);
  echo implode($newline,$payloads);
?>
````
- 由于MXHR响应报文越来越大，有必要每个资源收到是立即处理，而不是等待整个报文接收完成。这可以通过监听readyState3实现。
```js
        var req=new XHRHttpRequest();
        var getLatestPacketInterval,lastLengh=0;
        req.open('GET','');
        req.onreadystatechange=readyStateHandler;
        function readyStateHandler(){
            if(req.readyState===3&&getLatestPacketInterval==null){
                getLatestPacketInterval=setInterval(function(){
                    getLatestPacket();
                },25)
            }
            if(req.readyState===4){
                clearInterval(getLatestPacketInterval);
                etLatestPacket();
            }
        }

        function getLatestPacket(){
            var length=req.responseText.length;
            var packet=req.reaponseText.substring(lastLengh,length);
            processPacket(packet);
            lastLengh=length;
        }
```
- 以健壮的方式使用MXHR，有下面这个库：
   > http://techfoolery.com/mxhr/
   + 使用此技术有一些缺点，其中最大的缺点是以此方法获得的资源不能被浏览器缓存。如果你使用MXHR获取一个特定的css文件然后在下一个页面中正常加载它，它不在缓存中。因为整批资源是作为一个长字符串传输的，然后由JavaScript代码分隔。由于没有办法用程序将文件放入浏览器中，所以用这种方法获取的资源也无法存放在那里。 
   + 老版本IE6，7不正常readyState3或data:URL. 
   + 网站为每个页面使用了独一无二的打包的JavaScript或css文件以减少HTTP请求，因为它们对么个页面来说是唯一的，所以不需要从缓存中获取，除非重新载入特定页面。
   + 由于HTTP请求是Ajax中最极端的瓶颈之一，减少其需求数量对整个页面性能有很大影响。尤其是当你将100个图片转化为一个MXHR请求时，Adhoc在现代浏览器上测试了大量图片，其结果显示出此技术比逐个请求快了4到10倍。

### 发送数据  

- 有时你不关心接收数据，而只要将数据发送给服务器。可以发送用户的非私有信息以备日后分析，或者捕获所有脚本错误然后将有关细节发送给服务器进行记录和提示。当数据只需发送给服务器时，有两种广泛应用的技术：XHR和灯标
- HHRHttpRequest
   + 通过xhr对象的onerror事件可以在失败后重新提交数据。
   + 使用XHR将数据发回服务器时，get更快。这个是因为对少量数据来说，向服务器发送一个get请求要占用一个独立的数据包。而一个POST至少发送两个数据包，一个用于信息头。另一个用于POST体。POST更适合域向服务器发送大量数据，即因为它不额外额外数据包的数量，而get在url会限制长度。
 - 灯标
   + 此技术与动态脚本插入非常类似。JavaScript用于创建一个新的Image对象，将src设置为服务器上一个脚本的URL。此URL包含我们打算通过GET格式传回的键值对数据。注意并没有创建img元素或者将它们插入DOM中。 
   + (new Image()).src=url+'?'+params;
   + 服务器取得此数据并保存下来，而不必向客户端返回什么，因此没有实际的图像展示。这是将信息发回服务器的最有效方法。其开销很小，而且任何服务器端错误都不会影响客户端。
   + 可以监听Image对象的load事件，可以判断服务器是否成功收到了数据，通过检测服务器返回图片的宽度和高度（如果返回了一张图片）并用宽度或高度判断服务器的状态。（宽度为1表示“成功”，2表示“重试”）
   + 如果不需要为此响应数据，那么应当发送一个204 No Coutent响应代码，无消息正文。它将阻止客户端继续等待永远不会到来的消息体（后台设置）。
   + 灯标是向服务器回送数据最快和最有效的方法。服务器根本不需要发回任何响应正文，所以不比担心客户端下载数据。唯一的缺点就是接收到的响应类型是受限的。如果需要向客户端返回大量数据，那么使用XHR。如果只关心将数据发送到服务器端（可能需要减少的回复），那么使用图像灯标。

#### 数据格式总结

- 总的来说越轻量级的格式越好，最好是JSON和字符分隔的自定义格式（
\u0001等字符进行分割）。如果数据集很大或者解析时间成问题，那么就使用这两种格式之一。   
- JSON-P数据，用动态脚本标签插入法获取。它将数据视为可运行的JavaScript而不是字符串，解析速度极快。它能够跨域使用，但不应涉及敏感数据。
- 字符分割的自定义格式，使用XHR或动态脚本标签插入技术提取。使用split()解析。此技术在解析非常大数据集时比JSON-P技术略快，而且通常文件尺寸更小。

### Ajax性能导向

- 缓存数据
  + 最快的Ajaxqq就是你不要用到它。有两种主要方法避免发出一个不必要的请求：
  + 在服务器端，设置HTTP头，确保返回报文将被缓存在浏览器中。
  在客户端，于本地缓存已获取的数据，不要多次请求同一个数据。
  + 第一种就是最容易设置和维护，而第二个给你最大程度的控制。
- 设置HTTP头
  + Expires头设置浏览器应当缓存响应报文的时间。
  + 本地缓存数据
    - 除了依赖浏览器处理缓存之外，可以用手工方法实现它，直接存储那些从服务器收到的响应报文，将响应报文放在一个对象中，以URL为键值索引它。
    - 如果用户可能触发某些动作导致一个或多个报文报废，直接delete删除对应属性，十分方便。

####  了解Ajax库的限制

- 直接操作XHR对象减少了函数开销，进一步提高了性能，只是放弃使用Ajax库，可能会在古怪的浏览器上遇到一些问题。

#### 总结   

- 作为数据格式，纯文本和HTML是高度限制的，但它们可以节省客户端CPU周期。XML被广泛应用普遍支持，但它非常冗长且解析缓慢。 JSON是轻量级的，解析迅速（作为本地代码而不是字符串），交互性与XML相当。字符分割的自定义格式非常轻量，在大量数据集解析时速度最快，但需要编写额外的程序在服务器构造格式，并在客户端解析。
- 当从页面域请求数据时，XHR提供最完善的控制和灵活性，尽管它将所有传入数据视为一个字符串，这有可能降低解析速度。另一方面，动态脚本标签允许跨域请求和本地允许JavaScript，虽然它的接口不够安全，而且也不能读取信息头或响应报文。多部分XHR可减少请求的数量，可一次响应中处理不同文件类型  ，尽管它不能缓存收到的响应报文（是因为多个css文件打包在一起，其它页面有自己的css，复用性不高）。当发送数据时，图像灯标时最简单和最有效的方法。xhr也可用PSOT方法发送大量数据。 
- 减少请求数量，可通过JavaScript和css文件打包，或者使用MHXHR。
- 缩短网页的加载时间，在页面其它内容加载之后，使用Ajax获取少量重要文件。
- 确保代码错误不要直接显示给用户，并在服务器处理错误。
- 学会何时使用一个健壮的Ajax，何时编写自己的底层Ajax代码。

## 编程实践

#### 避免二次评估

- Javascript与许多脚本语言一样，允许你在程序中获取一个包含代码的字符串然后运行它。有四种标准方法可以实现：eval(),Function构造器,setTimeout(),setInterval()。每个函数都允许你传入一串字符串并运行它。
- 当你在JavaScript代码中执行（另一段）JavaScript代码时，你得付出二次评价得代价。此代码首先被评估正常代码，然后在执行过程中，运行字符串中得代码时发生另一次评估。二次评估是一项昂贵得操作，与直接包含相应代码相比占用更长时间。
   + 使用eval代替直接代码访问10000个数组项，在不同浏览器上得差异非常巨大。
   + 访问数组项时间上巨大差异，是因为每次使用eval时要创建一个新得解释/编译实例。同样得过程也发生在Function(),setTimeout,setInterval上，自动使代码执行速度变慢。
   + 避免二次评估使实现最优化得Javascript运行时性能得关键。

#### 使用对象/数组直接量

- 使用直接量赋值很快。作为一个额外的好处，直接量在代码中占用较少的空间，所以整个文件尺寸可以更小。
- 随着对象属性和数组项数的增加，使用直接量的好处也随之增加。直接量比创建一个对象在几乎所以的浏览器中都运行更快。

#### 延时加载

  - 本质上就是判断target.addEventListener是否存在，再把当前函数覆盖了。形成一个新函数，在最后调用一次。只要调用了一次，以后调用速度就快了。
  - 调用一个延时加载函数总是在第一次使用较长时间。
  - 延时加载适用于函数不会在页面上立即被用到的场合。
```js
 function addHandler(target,eventType,handler){
     if(target.addEventListener){
         addHandler=function(target,eventType,handler){

         }
     }else{
           addHandler=function(target,eventType,handler){

         }
     }
     addHandler();
 }
```

#### 条件预加载

- 除延时加载之外还有另一种方法称为条件预加载，它在脚本加载之前进行检测，而不是等待函数调用。这样做检测仍只做一次，但在此过程中来的更早。
- 条件预加载确保所有函数调用时间相同。其代价是在脚本加载时进行测试。预加载适用于一个函数马上就会被用到，而且整个页面生命周期中经常使用的场合。
````js
  var addHandler=document.body.addEventListener?function(){}:function(){}
````

#### 位操作运算符

 - 位掩码操作非常快，操作发生在系统底层，如果许多选项保存在一起并经常检查，位掩码有助于加快整体性能。

 #### 原生方法

 - 无论你怎么优化JavaScript代码，它永远不会比JavaScript引擎提供的原生方法更快。其原因十分简单：JavaScript的原生部分在你写代码之前它们就已经存在于浏览器中了，都是用底层语言写的，诸如c++。这意味着这些方法被编译成机器码，作为浏览器的一部分，不像你的JavaScript那样有那么多限制。
    + 当需要进行复杂计算时，首先查看Math对象。
    + 另一个例子时选择器API，可以像使用css选择器那样查询DOM文档。css查询被JavaScript原生实现并通过jQuery这个JavaScript库推广开来。Jquery引擎被认为是最快的css查询引擎，但它仍然比原生方法慢。原生的querySelector(),querySelectorAll()方法完成它们的任务时，平均只需要基于JavaScript的css查询的10%的时间。大多数JavaScript库已经使用了原生函数以提高它们的整体性能。
    + 当原生方法可用时，尽量使用它们，尤其时数学运算和DOM操作。用编译后的代码做越多的事情，你的代码就会越快。

 #### 总结
 
 - 通过避免使用eval和Function构造器避免二次评估。此外，给setTimeout和setInterval传递函数参数而不是字符串。
 - 创建新对象和数组时使用对象直接量和数组直接量。它们比非直接量形式创建和初始化更快。
 - 避免重复进行相同工作。当需要检查浏览器时，使用延迟加载或条件预加载。
 - 当执行数学运算时，考虑使用位操作，它直接在数字底层进行操作。
 - 原生方法总比JavaScript写的东西要快。尽量使用原生方法。

 ## 创建并运行部署高性能站点

 - 第一个也是最重要的提高网站速度的准则，特别针对那些第一次访问网站的用户，是减少渲染页面所需的HTTP请求的数量。因为合并资源通常能够以相当少的工作为用户赢得最大的潜在利益。
    + 一个简单的优化是将某些脚本合并成一个外部JavaScript文件，而不是全部，从而大大降低渲染页面所需HTTP请求的数量。
 - 使用内容投递网络
    + 内容传递网络（CDN）是按照地理分布的计数器网络，通过以太网负责向最终用户分发内容。使用CDN的重要原因是可靠性，可扩展性，但更重要的是性能。事实上，通过地理位置上最近的位置向客户提供服务，CDN可以极大的减少网络延迟
    + 一些大公司维护它们自己的CDN，但通常使用第三方CDN更经济一些。
    + 切换到CDN通常只需要改变少量代码，并可能极大的提高最终用户响应速度。

#### 总结

- 合并JavaScript文件，减少HTTP请求的数量
- 使用压缩工具处理JavaScript文件
- 以压缩形式提供JavaScript文件（gzip编码）
- 通过设置HTTP响应报文头使用JavaScript文件可缓存，通过向文件名附加时间戳解决缓存问题。
- 使用内容投递网络（CDN）提供JavaScript文件，CDN不仅可以提供性能，它还可以为你管理压缩和缓存。

## Tools工具

- Profiling 性能分析
   - 在脚本运行期间定时执行不同函数和操作，找出需要优化的部分。
- Network analysis 网络分析
   - 检查图片，样式表，和脚本的加载过程，汇报它们对整个页面加载和渲染的影响。
- JavaScript性能分析
````js
         var Timer={
            _data:{},
            start:function(key){
                Timer._data[key]=new Data();
            },
            stop:function(key){
                var time=Timer._data[key];
                if(time){
                    Timer._data[key]=new Data()-time;
                }
            },
            getTime:function(key){
                return Timer._data[key];
            }
        }
````   





<file_sep><!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>js高级</title>
	 <style>
        p {
            width: 100%;
        }
    </style>
</head>
<body>
	<div>
          <h2>JavaScript 高级 (6.10)</h2>
          <h4>相关资料推荐</h4>
          <ul>
          	<li>MDN-JavaScript: <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript">https://developer.mozilla.org/zh-CN/docs/Web/JavaScript</a></li>
          	<li>JavaScript 高级程序设计（第3版）建议平时有空就看,看书看到的是自己的</li>
          	<li>JavaScript 语言精粹 编写可维护的 JavaScript </li>
          	<li>你不知道的 JavaScript 上 中 下</li>
          	<li>JavaScript 权威指南 稍微有点儿难度</li>
          	<li>JavaScript 面向对象编程指南（第2版） JavaScript 面向对象精要</li>
          </ul>
          <h3> 二 基本概念复习</h3>
          <h4>JavaScript 是什么？</h4>
          <ul>
          	<li>JavaScript ( JS ) 是一种轻量级解释型的，或是 JIT 编译型的程序设计语言
                <ul>
                	<li>解释型：可以在执行阶段为变量赋值和修改数据类型</li>
                	<li>编译型：程序在执行之前就已经确定了类型，一旦定义类型，不可改变</li>
                	<li>后来一些 JavaScript 解析引擎为了提高 JavaScript 执行效率，引入了 JIT 即时编译器</li>
                	<li>执行之前先预编译，然后再执行的过程中再去动态解析</li>
                </ul>
          	</li>
          	<li>在 JavaScript 语言中最大的特色是什么？
              <ul>
              	<li>函数是一等公民</li>
              	<li>普通函数 构造函数 Object() Function() 当做参数来传递 函数自调用 当做返回值</li>
              	<li>apply、call、bind </li>
              	<li>函数内部的上下文 this 的指向  函数也对象</li>
              	
              </ul>
          	</li>
          	<li>有着 函数优先 (First-class Function) 的编程语言。</li>
          	<li>运行与浏览器，浏览器是 JavaScript 代码的执行环境或者说平台</li>
          	<li>JavaScript 除了可以运行在浏览器，也可以运行在别的环境</li>
          	<li>虽然它是作为开发web页面的脚本语言而出名的，但是在很多非浏览器环境中也使用JavaScript，例如 node.js 和 Apache CouchDB。</li>
          	<li>JS是一种基于原型、多范式的动态脚本语言，并且支持面向对象、命令式和声明式（如：函数式编程）编程风格。了解更多 关于JavaScript。
              <ul>
              	<li>面向对象 new Object()</li>
              	<li>原型</li>
              	<li>函数式编程 $('selector').css().html().on().css()...</li>
              </ul>
          	</li>
          	
          </ul>
          <h4>JavaScript 的组成</h4>
          <ul>
          	<li>JavaScript 中的组成，需要看 JavaScript 在哪个环境执行</li>
          	<li>如果 JavaScript 是运行在浏览器中：
             <ul>
             	<li>浏览器一般有两个引擎:渲染引擎;解析执行 JavaScript 代码的引擎</li>
             	<li>JavaScript 语言本身</li>
             	<li>浏览器环境为 JavaScript 提供的编程接口 BOM DOM</li>
             </ul>
          	</li>
           </ul>
           <h4>JavaScript 可以做什么</h4>
           <ul>
           	<li>语言本身:条件判断;函数;数组、对象、。。。循环。。</li>
           	<li>特定执行环境(浏览器):使用浏览器提供的编程接口;BOM DOM Ajax;地理定位;Web 存储</li>
           </ul>
           <h4>JavaScript 与 EcmaScript 的关系</h4>
           <ul>
           	<li>EcmaScript 是 JavaScript 的规范;各大浏览器厂商根据标准规范去做出相应的实现</li>
           	<li>JavaScript 是 EcmaScript 的实现 <ul>
           		<li>所以各大浏览器会有实现上的差异</li>
           		<li>Chrome 的 V8 解析引擎目前是公认的最快的</li>
           		<li>Node.js 也是基于 Chrome 的 V8 引擎制造的</li>
           	</ul></li>
             </ul>
             <h4>JavaScript 数据类型</h4>
             <ul>
             	<li>在将一个值赋给变量时，解析器必须确定这个值是基本类型值还是引用类型值。 JavaScript 变量可能包含两种不同数据类型的值：</li>
             	<li>JavaScript 中的基本类型（也叫作值类型）：
                   <ul>
                   	<li>Undefined</li>
                   	<li>Null</li>
                   	<li>Boolean</li>
                   	<li>Number</li>
                   	<li>String</li>
                   </ul>
             	</li>
             	<li>JavaScript 中的复合类型（引用类型）：
                 <ul>
                 	<li>Object Array Date RegExp Function</li>
                 	<li>基本包装类型 Boolean Number String</li>
                 	<li>单体内置对象 Global Math</li>
                 </ul>
             	</li>
             	<li>类型检测:typeof(简单类型) instanceof（数组）Object.prototype.toString.apply(引用类型)</li>
             </ul>
             <h4>基本类型与引用类型的差别</h4>
             <ul>
             	<li>基本类型在内存中占据固定大小的空间，因此被保存在栈内存中</li>
             	<li>从一个变量向另一个变量复制基本类型的值，复制的是值的副本</li>
             	<li>引用类型的值是对象，保存在堆内存</li>
             	<li>包含引用类型值的变量实际上包含的并不是对象本身，而是一个指向该对象的指针</li>
             	<li>从一个变量向另一个变量复制引用类型的值的时候，复制是引用指针，因此两个变量最终都指向同一个对象</li>
             </ul>
             <h4>面试题：深拷贝与浅拷贝</h4>
             <ul>
             	<li>面试题目 1：var a = {"x": 1};var b = a;a.x = 2;console.log(b.x);a = {"x": 3};console.log(b.x);a.x = 4;console.log(b.x);</li>
             	<li>面试题目 2：var a = {n: 1};var b = a;a.x = a = {n: 2};console.log(a.x);
console.log(b.x);</li>
            
             </ul>
             <h4>JavaScript 运算符优先级</h4>
             <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Operator_Precedence">MDN - 运算符优先级</a>
          <h3>三 JavaScript 面向对象编程</h3>
          <h4>什么是面向对象:</h4>
          <ul>
          	<li><a href="https://www.zhihu.com/question/19854505" target="_blank">知乎：如何用一句话说明什么是面向对象思想？</a></li>
          	<li><a href="https://www.zhihu.com/question/31021366" target="_blank">知乎：什么是面向对象编程思想？</a></li>
          	<li>面向对象与面向过程：<ul>
          		<li>面向过程就是亲力亲为，事无巨细，面面俱到，步步紧跟，有条不紊</li>
          		<li>面向对象就是找一个对象，指挥得结果</li>
          		<li>面向对象将执行者转变成指挥者</li>
          		<li>面向对象不是面向过程的替代，而是面向过程的封装</li>
          	</ul></li>
          	<li>面向对象的特性：抽象性;封装性;继承性;[多态性]</li>
          </ul>
          <h4>JavaScript 是不是一个面向对象的编程语言？</h4>
          <ul>
          	<li>不是:相对于传统的语言：Java、C#、C++ 之类的语言，JavaScript 中没有类的概念</li>
          	<li>是:JavaScript 里面也可以使用 new 语法来创建实例化对象，而且基本上都是对象：Object、Array、RegExp。。。</li>
          	<li>到底是什么:JavaScript 是一个多范式编程语言;面向过程;面向对象;函数式编程</li>
          </ul>
          <h4>什么是对象</h4>
          <ul>
          	<li>在实际开发中，对象是一个抽象的概念，可以将其简单理解为：数据集或功能集</li>
          	<li>ECMAScript-262 把对象定义为：无序属性的集合，其属性可以包含基本值、对象或者函数。 严格来讲，这就相当于说对象是一组没有特定顺序的值。对象的每个属性或方法都有一个名字，而每个名字都 映射到一个值。</li>
          	<li>提示：每个对象都是基于一个引用类型创建的，这些类型可以是系统内置的原生类型，也可以是开发人员自定义的类型。</li>
          	<li>总结：对象是一组键值对;对象本身就是封装的一种体现</li>
          </ul>
          <h4>创建对象</h4>
          <ul>
          	<li>原始模式的改进：工厂模式;通过工厂模式解决了创建多个相似对象代码冗余的问题， 但却没有解决对象识别的问题（即怎样知道一个对象的类型）。</li>
          	<li>工厂模式的改进：构造函数 </li>
          	<li>使用 constructor 属性可以用来确认实例和构造函数之间的关系,实例对象都具有一个 constructor 属性，可以获取创建该实例的构造函数  实例的 constructor 属性不严谨，推荐使用 instanceof 操作符</li>
          	
          </ul>
          <h4>原型对象 prototype</h4>
          <ul>
          <li>原型与继承:对构造函数来说，原型对象是它的一个属性。
对实例来说，原型对象是它的原型</li>
<li> 原型对象的一个应用:通过修改原型对象扩展原生构造函数内置方法</li>
          	<li>实例对象无法修改原型对象中的普通类型数据</li>
          	<li>如果实例去修改原型对象中的引用类型数据，就真的修改了</li>
          	<li>建议：将普通数据放到构造函数中，将函数放到原型中</li>
          	<li>__proto__属性为非标准属性，因此具有兼容问题</li>
          </ul>
          <h4>补充知识</h4>
          <ul>
          	<li>isPrototypeOf() 该用来判断，某个 proptotype 对象和某个实例之间的关系</li>
          	<li>hasOwnProperty()每个实例对象都有一个 hasOwnProperty() 方法，用来判断某一个属性到底是本地属性，还是继承自 prototype 对象的属性 </li>
          	<li>in 运算符 in 运算符可以用来判断，某个实例是否含有某个属性，不管是不是本地属性</li>
          	<li>自定义校验某个属性是否属于原型属性： hasProtypeProperty()</li>
          </ul>
          <h4>附录 A 代码规范</h4>
          <ul>
          	<li>代码风格
             <ul>
             	<li><a href="https://github.com/feross/standard">JavaScript Standard Style</a> </li>
             	<li><a href="https://github.com/airbnb/javascript">Airbnb JavaScript Style Guide() </a> </li>
             </ul>
          	</li>
          	<li> 校验工具 <ul>
             	<li><a href="https://github.com/douglascrockford/JSLint">JSLint</a> </li>
             	<li><a href="https://github.com/jshint/">jshint</a> </li>
             		<li><a href="https://github.com/eslint/eslint">ESLint</a> </li>
             </ul></li>
          </ul>
          <h4>附录 B Chrome 调试工具的使用</h4>
          <ul>
          	<li>调试面板各大功能菜单栏介绍</li>
          	<li>Elements  选择元素：Ctrl + Shift + c;切换设备：Ctrl + Shift + m</li>
          	<li>Console JavaScript REPL</li>
          	<li>Sources 查看网站资源文件资源 使用 Chrome 的 Sources 作为编辑器;在 Sources 面板按下 ESC 键可以查看切换 Console</li>
          	<li>Network 查看网络请求</li>
          	<li>Timeline 代码执行时间线</li>
          	<li>Profiles 查看分析网页性能</li>
          	<li>Application 查看执行环境资源，例如本地存储、Web 缓存、Web 数据库 等</li>
          	<li>Security Audits</li>
          	<li>断点调试
                 <ul>
                 	<li>设置断点:在具体代码的行号位置的侧边栏单击变为蓝色的标记;注意观察右侧 Breakpoints 可以管理我们设置的断点;启用和关闭所有断点：Ctrl + F8</li>
                 	<li>逐步执行:快捷键：F11 或者 Ctrl + ;浅蓝色表示已经执行过了，淡蓝色表示即将执行</li>
                 	<li>变量监视:右键变量 -> Add to watch;在 Watch 面板中添加要监视的成员</li>
                 	<li>逐过程执行:遇到普通语句和逐步的效果是一样的;遇到函数调用会跳过函数执行过程</li>
                 	<li>继续执行:遇到断点会挺住，否则直接执行结束</li>
                 	<li>条件断点</li>
                 </ul>
          	</li>
          </ul>
          <h4>附录 C 文档相关工具</h4>
          <ul>
          	<li>Markdown <a href="https://guides.github.com/features/mastering-markdown/">https://guides.github.com/features/mastering-markdown/</a><br>
          	<a href="https://github.github.com/gfm/">https://github.github.com/gfm/</a></li>
          	<li>电子书制作工具 <a href="https://github.com/QingWei-Li/docsify">https://github.com/QingWei-Li/docsify</a><br>
          	<a href="https://github.com/egoist/docute">https://github.com/egoist/docute</a></li>
          	<li>流程图工具：DiagramDesigner <a href="http://logicnet.dk/DiagramDesigner/">http://logicnet.dk/DiagramDesigner/</a></li>
          </ul>
          <h4>附录 D Markdown</h4>
          <ul>
          	<li>更加专注于写作，尤其程序员最喜欢用，表示代码很方便 类似于 HTML 的标记语言</li>
          	<li><a href="http://www.jianshu.com/p/q81RER" target="_blank">简书 - 献给写作者的 Markdown 新手指南</a></li>
          	<li><a href="http://wowubuntu.com/markdown/" target="_blank">Markdown 语法说明</a></li>
          	<li>leanote 写作工具</li>
          </ul>
          <h4>6.10目标</h4>
          <ul>
          	<li>1. 列举 JavaScript 中的普通数据类型和引用数据类型</li>
          	<li>普通数据类型：underfined,boolean,string,null,number 引用数据类型:Object Array Function  Date regExp</li>
          	<li>3. 概述基本类型和引用类型的差异并举例</li>
          	<li>基本类型在栈上存储值，引用类型在栈上存储值在堆上的引用的引用。</li>
          	<li>4. 概述什么是深拷贝和浅拷贝并举例</li>
          	<li> 深拷贝是通过循环和递归的手段对一个对象进行拷贝，新的对象与被拷贝的对象之间完全没有关系。浅拷贝只是单纯的通过循环被拷贝的对象的属性给新对象，如果被拷贝对象存在引用类型的属性，那么新对象的
   该属性和被拷贝对象的属性指向堆中的同一个内存地址。</li>
          	<li>5. 实现一个深度拷贝方法
             <p> function extend(obj,callObj){ callObj=callObj||{}; <br>
        for(var k in obj){  <br>
            if(Object.prototype.toString.call(obj[k])=="[object Object]") <br>
            {
                callObj[k]= arguments.callee(obj[k],{});
            } <br>else {
                callObj[k]=obj[k];
            }
        }
        return callObj;
    }</p>
          	</li>

          	<li>6. $.extend() 方法的使用
               <ul>
               	<li>浅拷贝用法  $.extend(obj, obj1, obj2);</li>
               	<li>深拷贝用法 $.extend(true, obj, obj1, obj2);</li>
               	<li>第一个参数要嘛给 true ，要嘛什么都不给 var returnVal = $.extend(false, obj, obj1, obj2);</li>
               	<li>jQuery.extend([deep], target, object1, [objectN])</li>
               	<li>target:一个对象，如果附加的对象被传递给这个方法将那么它将接收新的属性，如果它是唯一的参数将扩展jQuery的命名空间。object1:待合并到第一个对象的对象。objectN:待合并到第一个对象的对象。



</li>
               </ul>
          	</li>

          	<li>7. 普通数据类型检测和引用数据类型检测</li>
          	<li>普通数据类型:typeof 数组： instanceof  引用数据类型:Object.prototype.toStringApply()</li>
          	<li>8. 为系统内置构造函数添加原型方法</li>
          	<li>String.prototype.endStr=function(str){var reg=new RegExp(str+"$"),bl=false;
   <br> if(reg.test(this)) {bl=true;}return bl;}</li>
          	<li>9.jqery函数式编程
               <ul>
               	<li>function myJquery(target){ return new myJquery.prototype.inir(target);}</li>
               	<li> myJquery.prototype.inir.prototype=myJquery.prototype;</li>
               	<li>myJquery.prototype.inir=function(target){ var targets=  document.querySelectorAll(target);for (var i = 0; i < targets.length; i++) { this[i]=targets[i];} this.length=targets.length;}</li>
               
               </ul>
          	</li>
          	<li>10. 函数可以生成函数  function genCheckType(type) {return function (obj) {
     return Object.prototype.toString.apply(obj) === type}}</li>
          </ul>
          <h5>字符串方法： search 是强制正则的，而 indexOf 只是按字符串匹配的。match() 方法可在字符串内检索指定的值，或找到一个或多个正则表达式的匹配。</h5>
	</div>
	<div>
      <h2>面向对象封装，jq插件，深复制（6.11）</h2>
      <h4>今日目标</h4>
      <ul>
      	<li> 1. 概述构造函数-实例-原型对象 三者之间的关系并举例
      		<p>对于构造函数来说，原型对象就像是它的属性，原型对象中有一个属性指向构造函数。实例对象是通过构造函数创建的。实例对象中有一个属性—_proto_(该属性是非标准的属性)指向原型对象。</p>
      	</li>
      	<li>2. 概述什么是原型链以及对象属性的搜索原则并举例
      		<p>在js中，继承的实现体现在原型链中，当前对象的_proto_属性指向该对象的原型，而该对象的原型又有_proto_指向更上一级的原型
对象，直到_proto_属性指向null才结束。对象调用属性的时候，先在自身，如果没有，则沿着原型链一步步向上，如果找到就直接返回，
直到 Object.prototype.__proto__ 发现为 null 的时候，最后返回 undefined。就像dom对象的prototype。</p>
      	</li>
      	<li>3. 概述封装面向对象版标签页实现的演变过程
      		<p></p>
      	</li>
      	<li>4. 举例说明函数也是对象
      		<p>使用操作符Object.prototype.toString.call()返回"[object Function]"说明函数是函数对象，并且可以给函数添加属性，
并且通过函数名.属性可以进行调用。</p>
      	</li>
      	<li>
      		<p>5. 理解 jQuery 插件机制</p>
      	</li>
      	<li>
      		<p>6. 模拟实现一个 `$.extend()` 方法</p>
      	</li>
      </ul>
     <h4>函数优先级</h4>
     <ul>
     	<li>function Foo() {getName = function () {alert(1);};return this;}</li>
     	<li>Foo.getName = function () {this.foo = 'bar'; alert(2);};</li>
     	<li>Foo.prototype.getName = function () {alert(3);};</li>
     	<li>var getName = function () {alert(4);}; function getName() {alert(5);}</li>
     	<li>问：new Foo.getName();new Foo().getName();new new Foo().getName();</li>
     	<li>第三个解析：new ((new Foo()).getName)();</li>
     </ul>

    
	</div>

	<div>
  <h2>函数继承 （6.13）</h2>
      <h4>函数概念</h4>
      <ul>
      	<li>构造函数如果有返回值，则以引用类型为主</li>
      	<li>函数的三种角色：普通函数，构造函数，对象</li>
      	<li>把函数当作对象来使用，就必须只能通过 函数.成员 来访问</li>
      	<li>只要是通过 new 操作符来调用 Person 函数,则该函数在执行的的时候:
             <ul>
             	<li>1. 先创建一个新的对象 var o = {}</li>
             	<li>2. 将该函数内部的 this 指向 o ： this = o</li>
             	<li>3. 接下来开发人员就可以直接使用 this 来初始化 o 对象成员</li>
             	<li>4. 在函数的最后，默认隐式的 return this</li>
             </ul>
      	</li>
      	<li>什么是构造函数？
            <ul>
            	<li>从语法上，new 函数名() 就会把该函数当作构造函数来调用; 构造函数用来构建创建具体的对象的</li>
            	<li>为什么需要构造函数或者说构造函数解决什么问题？ 解决对象创建的问题; 语法简洁了;其次可以判断实例对象的类型了</li>
            	
            </ul>
      	</li>
      </ul>
      <h5>
      函数的 call 方法可以用来调用函数的同时，通过第一个参数改变内部 this 指向,需要传递参数，则可以从第二个参数开始以 , 作为分隔传递给函数</h5>
     
      <h4> 为什么通过匿名函数自执行把 window 传递进去</h4>
      <ul>
      	<li>从程序执行效率来讲：目的是为了减少内部的作用域查找范围</li>
      	<li>从代码依赖角度来讲：把相关的依赖项单独传递给匿名函数内部</li>
      	<li>这样做的目的是为了让模块的依赖更明确</li>
      </ul>
      <h4>数组some方法</h4>
      <ul>
      	<li> some 也是遍历数组，只不过它会针对函数的返回值得到不同的结果</li>
      	<li>如果在遍历的过程中某个函数返回了 true ，则 some 方法返回 true</li>
      	<li>如果一直遍历到结束，都没有函数返回 true，则 some 方法返回 false</li>
      	<li> var result = arr.Some(function (num, index) {return num % 2 === 0})</li>
      </ul>
        <h5>为什么需要对象?如果没有对象，可能就会有一大堆的全局变量;对象也叫命名空间;将一些相关数据放到一起，方便整理和维护;进行数据传递时，传递对象更加的方便</h5>
        <h5>jq:$(function(){}),方法的实现</h5>
        <h4>自己模拟实现 `map` `every` `filter`</h4>
        <ul>
        	<li>Array.prototype.map()方法创建一个新数组，其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。</li>
        	<li>every() 方法测试数组的所有元素是否都通过了指定函数的测试(与some的区别是some只要有一个成立，立马返回true)</li>
        	<li>filter() 方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。</li>
        </ul>
       <h3>继承二法</h3>
       <ul>
       	<li>第一种：组合遍历法 可以通过call继承父类的属性，通过循环遍历父类的prototype继承父类的方法（根据需求进行深赋值或浅复制）</li>
       	<li>第二种：组合寄生继承 可以通过call继承父类的属性,通过寄生的方式继承父类原型的方法
           <ul>
           <li>如果直接让子类的原型对象成为父类的实例，缺点是会继承父类的属性，所以采用一个中转构造函数，采用寄生的方式间接使子类的原型对象成为父类的实例</li>
           <li>如果直接让子类的原型对象指向父类的原型对象，修改子类constructor会把父类的也修改</li>

           	<li> function object(obj) {function F(){};F.prototype=obj;return new F();}</li>
           	<li>function inHeritPrototype(target,selector) {var prototype=object(selector);prototype.constructor=target;target.prototype=prototype;}</li>
           	<li><img width="400" src="img/组合寄生继承.png" alt=""></li>
           </ul>
       	</li>
       	<li>相同处：两者都可以继承父类的属性和方法，都是通过Call来继承父类的属性</li>
       	<li>不同点：第一种方法思路更加简单，第二种方法好处在于子类构造函数原型对象_proto_指向了父类的原型对象。</li>
       </ul>
	</div>
	<div>
         <h2>call,apply,bind,贪吃蛇（6.14）</h2>
         <h4>this指向的不同场景</h4>
         <ul>
         	<li>普通函数调用:
               <ul>
               	<li>在非严格模式下，普通函数中的 this 指向 window</li>
               	<li>在严格模式下，普通函数中的 this 是 undefined</li>
               </ul>
         	</li>
         	<li>
           构造函数调用
           <ul>
           	<li>指向实例对象</li>
           	<li>原型方法中的 this 与构造函数调用中的 this 指向相同</li>
           </ul>
         	</li>
         	<li>对象方法调用:指向调用对象，就是方法紧挨着的对象</li>
         	<li>事件绑定方法:指向绑定事件的对象</li>
         	<li> 定时器函数:指向 window</li>
         </ul>
          <h4>call,apply,bind的this指向方法的第一个参数（该参数必须是引用类型）</h4>
          <ul>
            <li>call 和 apply 特性一样
              <ul>
              	<li>都是用来调用函数，而且是立即调用</li>
              	<li>但是可以在调用函数的同时，通过第一个参数指定函数内部 `this` 的指向</li>
              	<li>call 调用的时候，参数必须以参数列表的形式进行传递，也就是以逗号分隔的方式依次传递即可</li>
              	<li>apply 调用的时候，参数必须是一个数组，然后在执行的时候，会将数组内部的元素一个一个拿出来，与形参一一对应进行传递</li>
              	<li>如果第一个参数指定了 `null` 或者 `undefined` 则内部 this 指向 window</li>
              </ul>
            </li>
            <li> bind
                <ul>
                	<li> 可以用来指定内部 this 的指向，然后生成一个改变了 this 指向的新的函数</li>
                	<li> 它和 call、apply 最大的区别是：bind 不会调用</li>
                	<li>bind 支持传递参数，它的传参方式比较特殊，一共有两个位置可以传递
                      <ul>
                      	<li>1. 在 bind 的同时，以参数列表的形式进行传递</li>
                      	<li> 2. 在调用的时候，以参数列表的形式进行传递</li>
                      	<li> 两者合并：bind 的时候传递的参数和调用的时候传递的参数会合并到一起，传递到函数内部</li>
                      	<li>函数的 bind 方法传递参数规则： 在 bind 的同时可以预制（预先传递）一些参数，不调用；然后在调用的时候，传递的参数，将会放到预制参数之后进行传递</li>
                      </ul>
                	</li>
                </ul>
            </li>
         
          	<li>针对bind绑定：绑定了 this 环境的 函数，如果使用 new 来调用，则 this 还是指向实例对象</li>
          </ul>
          <h5>面试题：模拟函数内部arguments[0]()调用参数方法 var arr=[function(){console.log(this.length)},2,3];arr[0]()</h5>
          <h5>Underscore.js:类似于jq库，jq操作dom，而它和dom无关，只要作用是对数组，对象一类操作的简化</h5>
	</div>
	<div><h2>贪吃蛇完成(6.16)</h2>
	<h4>原型模式和 借用父类构造函数+拷贝父类原型对象 的区别</h4>
	<ul>
		<li>原型模式继承核心就是利用原型链搜索机制实现继承，每一次访问继承过来的成员的时候都必须通过原型链进行查找，如果原型链太多，会导致效率不高。</li>
		<li>第一次继承的时候的效率高，而后续查找成员的时候效率低。</li>
		<li> 而 借用父类构造函数+拷贝父类原型对象 的方式，是直接将父类的所有成员拷贝到自己身上，每次使用的时候，直接就在自己身上拿到了，相对于原型模式效率更高</li>
		<li> 第一次继承的时候效率低，而后续查找成员的时候效率比原型模式高。</li>
		
	</ul>
    
	</div>
	<div>
       <h2>sea.js模块化（6.17）</h2>
       <h4>模块化解决的问题</h4>
       <ul>
       	<li>通过 script 标签加载 JavaScript 文件还是麻烦</li>
       	<li> 关键在于 JavaScript 脚本文件之间的相互依赖</li>
       	<li>文件越来越多，无法保证变量命名冲突的问题</li>
       </ul>
        <a href="https://seajs.github.io/seajs/docs/">SeaJS在线地址</a>
       <a href="https://github.com/seajs/seajs/archive/master.zip">下载地址</a>
       <h4>sea.js用法即注意事项</h4>
       <ul>
       	<li>引入 sea.js</li>
       	<li> 使用 seajs.use() 方法，指定模块启动入口，然后启动模块运行
            <ul>
            	<li> 提示：入口文件模块最好起名 main.js</li>
            	<li>注意：文件路径一定要以 ./ 或者 ../ 开头;如果不以 ./ 或者 ../ 则会跑到 sea.js 文件路径去查找</li>
            	
            </ul>
       	</li>
       	<li> 在 seajs 中，一个 JavaScript 文件就是一个模块
            <ul>
            	<li> 所有代码写到 define 函数中;所有代码都运行在模块私有作用域</li>
            	<li> define 函数需要接收一个匿名函数作为参数; 该函数参数同时需要接收三个参数：require exports  module</li>
            	<li>注意：参数顺序不能变，包括参数的名字不能改</li>
            </ul>
       	</li>
       	<li>
         使用 require 函数根据指定文件模块路径加载模块,返回加载模块的返回值（函数，对象）
         <ul>
         	<li>模块的后缀名可以省略</li>
         	<li>加载并执行被加载模块的代码</li>
         	<li>返回当前文件模块的值 module.exports= 返回对象（函数，值），赋值给它可以使其它js模块引用当前模块获取返回值</li>
         </ul>
       	</li>
       	<li>define(function (require, exports, module) {  var $ = require('../vendor/jquery-3.2.1')//引入 module.exports = 返回对象;//导出  })</li>
       	<li>jq和underscore不支持sea.js(cmd)，而支持require.js（amd）;要找到对应的define手动加上</li>
       	<li>exports本质上就是module.exports，指向同一个地址。但后者更好用，因为前者不能修改其地址的引用，而后者可以</li>
       </ul>
       <h4>模块进化论</h4>
       <ul>
       	<li>全局函数：容易导致命名空间污染，多个函数之间没有直接关系</li>
       	<li>命名空间：将多个函数放到 cal 对象命名空间中，让他们产生了一定的联系；对象命名空间中全部都暴露出来了，能不能有一种方式，让对象具有私有空间</li>
       	 	<li>私有空间-将对象封装到函数中的作用：
            <ul>
            	<li> 具有了私有空间，公有可以直接挂载到 cal 上即可，私有的直接定义到内部函数作用域即可</li>
            	<li>如果希望外部可以访问内部的私有成员而不允许修改，则可以直接为对象提供一个方法，在方法内部返回当前作用域内部的某个私有成员</li>
            </ul>
       	</li>
       	
       	<li>模块的依赖问题-关于模块的传参：
             <ul>
             	<li>1. 从代码执行效率上来讲，能提高内部作用域查找效率;</li>
             	<li> 2. 从模块维护的意义上来讲，让模块的依赖看起来更明确</li>
             </ul>
       	 </li>
      
       
       </ul>
      
	</div>
	<div>
         <h2>事件,闭包,作用域,伪数组(6.19)</h2>  
         <h4>伪数组</h4>
         <ul>
         	<li> 所有的内置构造函数都继承自 Object.prototype; 也就是说所有数组都具有 Object.prototype 的成员; 注意：这里主要是为了说明：所有普通对象没有 Array.prototype 的方法</li>
         	<li>伪数组是一个类似于数组结构的对象</li>
         	<li> Array.prptotype.slice.call(伪数组, 参数...)</li>
         	<li>简写形式：[].数组方法.call(伪数组, 参数);[].数组方法.bind(伪数组, [参数..])</li>
         	<li>数组和对象的区别
             <ul>
             	<li>一个按键值来访问读取数据;一个按索引来访问读取数据</li>
             	<li>对象没有 length 属性;数组具有 length 属性，而且会随着成员长度的变化而变化</li>
             	<li>什么是伪数组：var obj={0:'a',1:'b',length；2}
             	结构类似于数组的对象，就是伪数组，但是不能使用数组的方法
             	</li>
             	<li>伪数组借用数组方法原理：Array.prototype.myEach = function (callback) { for(var i = 0; i < this.length; i++) { callback(this[i], i)}}</li>
             </ul>
         	</li>
         </ul>
          <h4>函数的其它成员</h4>
          <ul>
          	<li> arguments 实参列表，是一个伪数组</li>
          	<li> arguments 除了是一个伪数组之外，还有一个特殊的属性：callee;callee 指向当前函数本身</li>
          	<li> caller 可以得到该函数的调用者(就是当函数在其它函数中时调用，包裹的函数就是调用者)</li>
          	<li>length 形参的个数</li>
          	<li>name   函数的名称</li>
          </ul>
         
          <h4>预解析</h4>
          <ul>
          	<li>全局预解析（所有变量和函数声明都会提前；同名的函数和变量函数的优先级高</li>
          	<li>函数内部预解析（所有的变量、函数和形参都会参与预解析）</li>
          	<li>函数只有在调用执行的时候才会预解析</li>
          </ul>
         <h5>闭包的应用练习，js执行机制讲解：任务队列栈（先进先出）</h5>
         <h4>阻止冒泡和默认行为</h4>
         <ul>
         	<li> 使用 return false 阻止事件默认行为，只针对 DOM 0 级事件绑定有效，对 DOM 2 级事件绑定无效</li>
         	<li> 阻止事件冒泡，无论是 0 级 和是 2 级事件绑定都必须使用 e.stopPropagation() 方法</li>
         	<li> 关于阻止事件冒泡，必须使用 e.stopPropagation() 方法</li>
         </ul>
         <h5>自定义事件简单例子；正则音乐播放器例子；递归菜单树例子</h5>
        
	</div>

	<div>
     <h2>canvas(6.21)</h2>
     <h4>canvas基本概念</h4>
     <ul>
     	<li>canvas画布的默认匡高300X150,canvas的宽度和高度不允许使用样式设置，要通过标签属性设置</li>
     	<li>canvas坐标系：原点在左上角，x轴水平向右，y轴垂直向下</li>
     	<li>获取画布: var cas = document.querySelector('canvas'); 获取画图工具: var ctx = cas.getContext('2d');// webgl</li>
     	<li>canvas属性
         <ul>
         	<li>绘制起点：ctx.moveTo(100,100);</li>
         	<li>绘制路径： ctx.lineTo(200,100);线条宽度：ctx.lineWidth = 10; 线宽，默认1px</li>
         	<li>描边 ctx.stroke(); 描边颜色： ctx.strokeStyle = 'red';</li>
         	<li>填充路径： ctx.fill();填充颜色： ctx.fillStyle = 'green';</li>
         	<li>关于路径的闭合：1、closePath自动闭合路径（推荐） 2、手动闭合有可能会缺角，解决办法就是多绘制一个点</li>
         	<li>线末端类型(butt默认)： ctx.lineCap = 'square'（比起默认来等于在线条两头各加了一个方块）; ctx.lineCap = 'round'（比起默认来等于在线条两头各加了一个半圆）;</li>
         	<li>相交线的拐点(miter默认)：ctx.lineJoin = 'bevel';（横切，默认直角被切平）ctx.lineJoin = 'round';（直角变圆角）</li>
         	<li>开启新的路径(保证不同的路径样式不同):ctx.beginPath();</li>
         	<li> 虚线绘制 <ul>
         		<li>设置虚线： ctx.setLineDash([5,10,15,20]);可以设置任意多个，按照设置的长度一个虚线一个空白循环显示</li>
         		<li>获取虚线宽度集合：ctx.getLineDash() 奇数获取两组一样的，偶数获取一组。</li>
         		<li>设置虚线偏移量（负值向右偏移） ctx.lineDashOffset = -3;</li>
         		
         	</ul></li>
         	<li>状态的保存与恢复
             <ul>
             	<li>save() 作用就是保存当前环境（上下文），本质上就是保存当前的一些样式设置</li>
             	<li>restore() 恢复最近一次保存的环境</li>
             	<li>可以保持多个当前环境采用的是栈stack(后进先出)的模式</li>
             
             </ul>
         	</li>
         </ul>
     	</li>
     	<li>非零环绕规则
            <ul>
            	<li>路径围住的所有区域，从任何一个区域内部向外拉出一条直线</li>
            	<li>如果该直线穿过的线条数量为奇数条，那么一定会填充</li>
            	<li>如果该直线穿过的线条数量为偶数条，并且方向相反的线条条数相同的话就不填充</li>
            	<li></li>
            </ul>
     	</li>
     	<li>canvas绘制一个像素实际上用的连个像素来表示，对于斜线来说，也是用两个像素表示，但是重合了一个像素，所以看上去比较浅</li>
     	<li>canvas画布可以理解为两个尺寸：标签尺寸、画布尺寸
          <ul>
          	<li> 通过标签内部的属性设置宽高，那么这两种尺寸都会被设置</li>
          	<li>  如果通过样式设置宽高，那么只会设置元素尺寸，浏览器会自动拉伸画布去适应标签尺寸</li>
          	<li>画布尺寸：ctx.canvas.width</li>
          
          </ul>
     	</li>
     	
     </ul>
     <h5>绘制折线图，颜色渐变等练习</h5>
	</div>
  <div>
        <h2>饼状图(6.22)</h2>
         <h4>矩形</h4>
         <ul>
           <li>画一个矩形的路径（没有独立路径）：ctx.rect(x,y,width,height);ctx.stroke();</li>
           <li>画一个描边矩形（有独立路径，不影响别的绘制）：ctx.strokeRect(x,y,width,height);</li>
           <li>画一个填充矩形（有独立路径，不影响别的绘制）：ctx.fillRect(x,y,width,height);</li>
           <li>清除画布一块区域：ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height);</li>
          </ul>
          <h5>线性渐变</h5>
          <ul>
            <li>var gradient = ctx.createLinearGradient(0,0,0,100); gradient.addColorStop(0,'red'); gradient.addColorStop(1,'pink'); ctx.fillStyle = gradient;</li>
            <li>径向渐变 ctx.createRadialGradient()</li>
          
          </ul>
            <h4>圆弧绘制（radian 弧度;angle 角度）</h4>
            <ul>
              <li>角度：一个圆周均分360份，一份就是1度;弧度：如果圆弧的长度等于半径的长度，那么此时该圆弧所对应的角度就是1弧度</li>
              <li>一度等于多少弧度:var per = 2*Math.PI/360;</li>
              <li> ctx.arc(x,y,r,开始弧度,结束弧度,true(默认false表示顺时针；true表示逆时针));</li>
              <li>圆弧绘制:artTo方法需要三个点：需要一个起点，参数中需要两个点，这三个点形成两条切线，然后结合半径就能决定圆弧的位置</li>
              <li>ctx.moveTo(150, 20);ctx.arcTo(150,100,50,20,50);ctx.stroke();</li>
            </ul>
           <h4>绘制文本</h4>
           <ul>
             <li>ctx.font = '12px 微软雅黑' 设置字体</li>
             <li>描边字体 strokeText()</li>
             <li>填充字体 fillText(text,x,y,maxWidth)
                <ul>
                  <li>text 要绘制的文本</li>
                  <li>x,y 文本绘制的坐标（文本左下角）</li>
                  <li>maxWidth 设置文本最大宽度，可选参数</li>
                </ul>
             </li>
             <li>ctx.textAlign文本水平对齐方式，相对绘制坐标来说的；
              <ul>
                <li>left；center；right；start 默认；end；</li>
                <li>direction属性css(rtl ltr) start和end于此相关;如果是ltr,start和left表现一致;如果是rtl,start和right表现一致</li>
              </ul>
             </li>
             <li>ctx.textBaseline 设置基线（垂直对齐方式 ）
                <ul>
                  <li>top 文本的基线处于文本的正上方，并且有一段距离</li>
                  <li>middle 文本的基线处于文本的正中间</li>
                  <li>bottom 文本的基线处于文本的证下发，并且有一段距离</li>
                  <li>hanging 文本的基线处于文本的正上方，并且和文本粘合</li>
                  <li>alphabetic 默认值，基线处于文本的下方，并且穿过文字</li>
                  <li>ideographic 和bottom相似，但是不一样</li>
                </ul>
             </li>
             <li>measureText() 获取文本宽度obj.width</li>
           
           </ul>
           <h4> 实例</h4>
           <ul>
             <li>绘制渐变彩虹</li>
             <li>绘制饼图</li>
           
           </ul>
  </div>
  <div>
       <h2>canvas基础动画(6.23)</h2>
       <h4>drawImage</h4>
       <ul>
         <li>三个参数drawImage(img,x,y)
           <ul>
             <li>img 图片对象、canvas对象、video对象</li>
             <li> x,y 图片绘制的左上角</li>
           </ul>
         </li>
         <li>
             五个参数drawImage(img,x,y,w,h)
             <ul>
               <li> img 图片对象、canvas对象、video对象</li>
               <li> x,y 图片绘制的左上角</li>
               <li>w,h 图片绘制尺寸设置(图片缩放，不是截取)</li>
             </ul>
         </li>
         <li>
             九个参数drawImage(img,x,y,w,h,x1,y1,w1,h1)
             <ul>
               <li>img 图片对象、canvas对象、video对象</li>
               <li></li>
               <li>x,y,w,h 图片中的一个矩形区域; x1,y1,w1,h1 画布中的一个矩形区域</li>
             </ul>
         </li>
       </ul>
       <h5>绘制多张图片时load加载问题：可以设置一个标识，每次加载完成一张图片，标识加一，当标识数量等于图片数量时，执行回掉函数</h5>
       <h5>创建的javascript标签在添加到页面才会加载，创建的img标签给它路径后就会加载</h5>
       <h4>定时函数</h4>
       <ul>
         <li>根据浏览器自适应进行帧绘制，有兼容性问题（部分浏览器默认大约1秒60下）function action(){requestAnimationFrame(action);}</li>
         <li> 事件队列中的任务何时执行？1、满足特定的条件2、主线程必须是空闲的</li>
         <li>js的运行时单线程的，浏览器是多线程的</li>
         <li> 函数的嵌套形成闭包；闭包包括内层函数和内存函数所处的环境（外层函数的作用域）</li>
         <li> 闭包可以延长变量的生命周期; 闭包可以缓存一些中间状态值</li>
       </ul>
       <h4>坐标变换（改变的是坐标的原点）</h4>
       <ul>
         <li>坐标系原点移动： ctx.translate(x,y);</li>
         <li>坐标系放大缩小 ctx.scale(x,y);参数表示宽高的缩放比例</li>
         <li>坐标系旋转 ctx.rotate(-Math.PI/4);参数表示旋转弧度</li>
         <li>在坐标系改变后，要清除画布的内容，可以用状态保存(save)，和状态恢复(restore)，就是坐标系修改前保存，修改后复原状态</li>
       
       </ul>
       <h4>实例</h4>
       <ul>
         <li>绘制精灵图逐帧动画</li>
         <li>通过键盘控制精灵图移动</li>
       </ul>
  </div>
  <div>
       <h2>像素鸟(6.25)</h2>
       <h5>判断一个坐标是否处于路径上：ctx.isPointInStroke(x,y)</h5>

       <ul>
         <li>对象的本质就是：无序的键值对集合</li>
         <li>所有的函数都是Function的实例对象</li>
         <li>所有的实例对象中都有一个__proto__属性，但是__proto__属性在实际开发中不可以使用，该属性是浏览器内部使用的，并且该属性并不是标准属性，该属性本质上就是创建该实例的构造函数的原型对象（prototype本质上就是一个对象---可以理解为Object的实例对象 {} new Object()）</li>
       
       </ul>
       
  </div>
</body>
</html><file_sep>### 第一天
- 1.请写出jquery绑定事件的方法，至少两种
   + bind('事件类型',function(){});
   + delegate('','',function(){}),注册委托事件
   + on('事件类型,'委托对象',function(){});jquery对绑定方法进行了整合成为on。
- 2.判断js对象为空的方法，判断数组为空的方法？
   + 使用JSON.parse进行转换。
   + Object.keys(obj).length，Object.keys()ie9下不兼容，可以使用for-in加hasOwnProperty和数组可以通过.length属性进行判断。
- 3.li和li之间有看不见的空白间隔时什么原因引起的，怎么解决？ 
   + 是因为有默认的margin值，可以把外边距设置为0。
   + 距离可能是换行后的空白节点，可以设置li{ float: left; list-style: none;}解决。
- 4.什么是前端路由，什么时候用前端路由？优点和缺点
   + 什么是前端路由？
       - 路由是根据不同的 url 地址展示不同的内容或页面
       - 前端路由就是把不同路由对应不同的内容或页面的任务交给前端来做，之前是通过服务端根据 url 的不同返回不同的页面实现的
       
   + 什么时候使用前端路由？   
       - 在单页面应用，大部分页面结构不变，只改变部分内容的使用
   + 前端路由有什么优点和缺点？  
       - 优点
            - 用户体验好，不需要每次都从服务器全部获取，快速展现给用户
       - 缺点   
            - 使用浏览器的前进，后退键的时候会重新发送请求，没有合理地利用缓存
            - 单页面无法记住之前滚动的位置，无法在前进，后退的时候记住滚动的位置  

- 5.理想视口有哪几个属性？
   + name='viewport':告诉浏览器要对当前页面做哪些设置
   + content：要对视口的哪些内容进行设置。
   + initial-scale：页面初始化的时候放大的倍数。
   + user-scale:页面允许用户缩放页面 yes or no
   + maxmum-scale：如果允许的话：可以放大的倍数。
   + minimum-scale：如果允许放大的话，最小的倍数。
- 6.如何异步加载脚本
   + 动态创建脚本进行加载。
   + 使用iframe窗体加载脚本。
   + 给标签使用defer和async属性。        <file_sep><title>node笔记</title>

## 环境配置 基本概念 node基本语法及ES6(7.24)

#### 环境配置

1. 下载：[nvm-windows](https://github.com/coreybutler/nvm-windows/releases/download/1.1.6/nvm-noinstall.zip)
2. 解压到一个全英文路径
3. 在全英文路径下新建 `settings.txt` 文件，把下面配置考入
  ```
  root: <nvm.exe 所在的目录，例如：C:\Develop\nvm>
  arch: 64
  proxy: none
  originalpath: .
  originalversion:
  node_mirror: https://npm.taobao.org/mirrors/node/
  npm_mirror: https://npm.taobao.org/mirrors/npm/
  ```
4. 配置环境变量 可以通过 `window+r` 运行 `sysdm.cpl`
  + 环境变量：就是操作系统中定义变量的地方。通过配置允许程序在电脑上可以快捷访问的范围。
  + `NVM_HOME = <当前 nvm.exe 所在目录>`
  + `NVM_SYMLINK = <node 快捷方式所在的目录>`
  + `PATH += %NVM_HOME%;%NVM_SYMLINK%;`
  + 打开CMD通过 `set [name]` 命令查看环境变量的值或set Path查看全部环境变量
  + PowerShell中是通过 `dir env:[name]` 命令
5. NVM使用说明(Node Version Manager（Node 版本管理工具）)：
  + https://github.com/coreybutler/nvm-windows
6. NVM基本操作
   + nvm use <version>切换node版本
   + nvm install <version>插入node版本
   + nvm version 输出所有版本
   + node -v 输出当前版本
   + nvm list available 输出镜像库里所有的版本

#### 知识点

-  <>：必填参数  
-  []：选填参数
-  函数提升是历史原因，以前的浏览器内存小，提升到顶部一次开辟空间。要是不够内存直接就会出问题，而不用运行到一定位置后在报错。
- shift ?可以显式git站点中所有快捷键
- angular1中使用的是定时器轮询实现数据双向绑定。vue和angular2使用Object.defineProperty()方法实现数据的双向绑定。
 > https://developer.mozilla.org/zh-CN/docs/Web
- DEBUG vscode中自带的js运行环境 
- 配置md文件转html文件步骤
   + 先找一个解析md的模块 
   > https://github.com/chjj/marked
   + 再加上githab.css

#### node基本语法

1. node知识点
  - 全局根对象 global
  - Node.js 是一个 JavaScript 的运行平台，不是一门语言，更不是 JavaScript 的框架。
  - 在node环境中运行js文件，它会把js文件当成一个模块，伪全局变量指的是node在运行当前js文件时给这个文件传入的对象，并不是真的全局对象。
  - REPL 是一个类似于 Console 的东西，用于测试小量代码
  > - 官方文档：https://nodejs.org/dist/latest-v8.x/docs/api/
  > - 国内文档镜像：https://npm.taobao.org/mirrors/node/latest/docs/api/index.html
  > - 全局对象（顶层对象）
  https://npm.taobao.org/mirrors/node/latest/docs/api/globals.html
  - REPL环境-Mancy，类比于console.log，输出输入，进入下一次
  - process进程对象，类比于浏览器中this。
  - 第三方模块 loadsh 封装一些数组对象操作函数
2. node在cmd中操作
  - .exit 退出node

#### 辅助成员

- `console`
- `setInterval`
- `setTimeout`

#### 进程相关

- `process`
  + `process.stdin` 获取用户输入，回车结束
  + `process.stdout`
    - 一个指向标准输出流(stdout)的 可写的流(Writable Stream)：
    - process.stdout.write('这是一行数据\n这是第二行数据');
  + `process.argv`  获取执行模块用户传入参数
  + `process.env`
    - process.env.NODE_ENV === 'production'
    - 判断是否是生产环境
  + `process.cwd()` 当前目录
  + `process.title`  修改命令框title
  + `process.versions`   
    - 一个暴露存储 node 以及其依赖包 版本信息的属性
  + `uncaughtException event`   全局异常捕获

#### 路径相关（伪全局）

- `__dirname`  当前路径
- `__filename` 当前文件路径（多一个文件名称）

#### 模块相关（伪全局）

- `module`
- `exports`
- `require()` 引入模块

#### ES6
- let定义的是一个可变的量
- const 定义一个常量
- 模板字符串  `${}`
- 箭头函数没有this的说法，箭头函数不会改变this的指向
   

## Common JS 模块 文件操作练习(7.26)

- Node REPL 环境
  - https://github.com/princejwesley/Mancy
- inspect 调试 下一步n
### 模块化开发

- Node 采用的模块化结构是按照 Common JS 规范
- 模块与文件是一一对应关系，即加载一个模块，实际上就是加载对应的一个模块文件。
- 关于 Common JS 规范（了解）
  > http://wiki.commonjs.org/wiki/CommonJS
- Common JS 模块特点 
  + 所有代码都运行在模块作用域（本质上是自执行函数），不会污染全局作用域。
  + 模块可以多次加载，但是只会在第一次加载时运行一次，然后运行结果就被缓存了，以后再加载，就直接读取缓存结果。如果想让模块再次运行，必须清除缓存。
  + delete require.cache['C:\\Users\\zce\\Desktop\\day-02\\code\\lib\\calc.js']
  + 模块加载的顺序，按照其在代码中出现的顺序。
  + 定义一个模块
     - 一个新的 JS 文件就是一个模块，包括入口文件；
     - 一个合格的模块应该是有导出成员的，否则模块就失去了定义的价值；
     - 模块内部是一个独立（封闭）的作用域（模块与模块之间不会冲突）；
     - 模块之间必须通过导出或导入的方式协同；
- 如果我们需要载入一个自己写的 JS 文件，路径必须采用点开头的路径
- 模块内部定义的成员只能在模块内部使用,除非挂载到全局对象上(global)
#### 导出方式：

     - 导出单个成员:  exports.name = value;
     - 导出整体对象: module.exports = { name: value };
  +  module.exports 和 exports
     - exports 是指向 module.exports 的别名，相当于在模块开始的时候执行
        + var exports = module.exports;
     - 最终模块的导出成员以 module.exports 为准

#### 载入模块

- Node 使用 Common JS 模块规范，内置的 require 函数用于加载模块文件。
   + require 的基本功能是，读入并执行一个 JavaScript 文件，然后返回该模块的 exports 对象
   + 如果没有发现指定模块文件就会报错。
- require 参数（模块路径）
   + `.`：相对路径方式加载模块文件
     - const module1=require('./calculator')
     + start with .:./ ../
        - 都是按照相对路径方式找到文件
        -  相对路径得考虑基准路径
   + `/`：绝对路径方式加载文件
     - 没有可移植性,基本不会用
     - const module2=require('C:/czbkqd/就业班/笔记/node/lib/require.js')
   
   + other：加载预定义模块
     - const fs=require('fs');
   + 可省略的情况：
      - 扩展名是 `.js`、`.json`
      - require 可以用来载入 JSON 文件，返回对象
      + 文件名是 `index`index和扩展名可以默认不写
        - 如果有和目录同名得文件，则找这个文件
- require.main 可以用来获取入口模块

#### require实现原理   

```js
function $require(x){
    //1.根据文件路径找到js文件
    const fs=require('fs');
    const path=x
    //2.读取文件内容
    const content=fs.readFileSync(path,'utf8')
    //3.执行代码
    const $module={
        $exports:{}
    }
    const $exports=$module.$exports
    const code=`(function($exports,$module,$require){
         ${content}
    }($exports,$module,$require))
     
    `
   eval(code);
    //4.想办法拿到module.exports
    //4.return
    return $module.$exports;
}
```      

#### 常用内置模块

- [path](http://nodejs.org/api/path.html)：处理文件路径。
- [fs](http://nodejs.org/api/fs.html)：操作文件系统。
- [url](http://nodejs.org/api/url.html)：用于解析 URL。
- [http](http://nodejs.org/api/http.html)：提供 HTTP 服务器功能。
- [querystring](http://nodejs.org/api/querystring.html)：解析 URL 中的查询字符串。
- [util](http://nodejs.org/api/util.html)：提供一系列实用小工具。
- [其他](https://nodejs.org/api/)

---  

#### require 的缓存 

### 文件操作

#### path 模块
- path不会判断路径存在与否，只是单纯得字符串操作。
+ path.join() 
   - join只是单纯得拼接
+ path.resolve() 
   - resolve会在前一个路径作为基准路径，然后cd操作 后面绝对路径会覆盖第一个
- path.basename() -- 获取文件名（可以忽略扩展名）
- path.dirname() -- 获取路径中的目录部分
- path.extname() -- 获取扩展名部分
- path.isAbsolute() -- 判断是否为绝对路径
- path.normalize() -- 正常化路径（格式）
- path.relative() -- 获取第二个目录相对于第一个目录的相对路径
- path.parse() -- 类似于 JSON.parse
- path.format() -- 类似于 JSON.stringify

```js
console.log('join用于拼接多个路径部分，并转化为正常格式');
const temp = path.join(__dirname, '..', 'lyrics', './友谊之光.lrc');
console.log(temp);

console.log('获取路径中的文件名');
console.log(path.basename(temp));

console.log('获取路径中的文件名并排除扩展名');
console.log(path.basename(temp, '.lrc'));

console.log('====================================');

console.log('获取不同操作系统的路径分隔符');
console.log(process.platform + '的分隔符为 ' + path.delimiter);

console.log('一般用于分割环境变量');
console.log(process.env.PATH.split(path.delimiter));

console.log('====================================');

console.log('获取一个路径中的目录部分');
console.log(path.dirname(temp));

console.log('====================================');

console.log('获取一个路径中最后的扩展名');
console.log(path.extname(temp));

console.log('====================================');

console.log('将一个路径解析成一个对象的形式');
const pathObject = path.parse(temp);
console.log(pathObject);

console.log('====================================');

console.log('将一个路径对象再转换为一个字符串的形式');
// pathObject.name = '我终于失去了你';
pathObject.base = '我终于失去了你.lrc';
console.log(pathObject);

console.log(path.format(pathObject));

console.log('====================================');

console.log('获取一个路径是不是绝对路径');
console.log(path.isAbsolute(temp));
console.log(path.isAbsolute('../lyrics/爱的代价.lrc'));

console.log('====================================');

console.log('将一个路径转换为当前系统默认的标准格式，并解析其中的./和../');
console.log(path.normalize('c:/develop/demo\\hello/../world/./a.txt'));

console.log('====================================');

console.log('获取第二个路径相对第一个路径的相对路径');
console.log(path.relative(__dirname, temp));

console.log('====================================');

console.log('以类似命令行cd命令的方式拼接路径');
console.log(path.resolve(temp, 'c:/', './develop', '../application'));

console.log('====================================');

console.log('获取不同平台中路径的分隔符（默认）');
console.log(path.sep);

console.log('====================================');

console.log('允许在任意平台下以WIN32的方法调用PATH对象');
// console.log(path.win32);
console.log(path === path.win32);

console.log('====================================');

console.log('允许在任意平台下以POSIX的方法调用PATH对象');
console.log(path === path.posix);
```

### fs 模块

#### 同步方式读写

- `fs.readdirSync(path[, options])`  获取目录下的文件名称
- `fs.readFileSync(path[, options])` 同步读取文件内容
- `fs.writeFileSync(file, data[, options])` 同步写入
- fs.renameSync 修改文件名称
- fs.statSync 获取文件状态



#### 异步方式读写

- `fs.readdir(path[, options], callback)`
- `fs.readFile(path[, options], callback)`
- `fs.writeFile(file, data[, options], callback)`



#### 对比两者差异

异步编程与同步编程各自优缺点

错误处理使用 trycatch
错误处理使用 回调函数的第一个参数
- 性能
  - 异步 win
- 代码可读性
  - 同步 win
- 异常处理
  - 同步 win  

### fs 案例

- 批量文件批量重命名
- 歌词文件读取并解析
  - GBK 编码通过 iconv-lite模块 转换
  - /\[(\d{2})\:(\d{2})\.(\d{2})\]\s(.+)/
- 字符画动画
  - \u001b[2J\u001b[0;0H
  - clear 模块


#### 知识点
- 谷歌换浏览器图片插件 pejkokffkapolfffcgbmdmhdelanoaih 
- 传入图片尺寸返回图片
  > https://unsplash.it/800/800?random    
- node原生API不够满足需求时，可以c++ 写  
- normalize.css 对样式进行初始化。
- process.on('exit',)  程序最后执行事件 
#### 老师信息
  - 13241087977
  - <EMAIL>
  > https://github.com/zce




## NPM NRM NVM node单线程原理(7.27)

- npm registry(注册表)node包的管理工具
- nvm node版本管理器
- nrm 镜像管理工具，管理npm拉取包的地址
  - nrm ls
  - 可以用来切换npm镜像的源
  - nrm use npm 
+ node --
  - repl类似浏览器的输出调试框,可以直接在cmd中输入js执行
  - 启动一个js文件： `node path/to/text.js`
  - ~~ `node inspect path/to/text.js` ~~  

### NPM （node.js package management）

- 全球最大的模块生态系统，里面所有的模块都是开源免费的；也是Node.js的包管理工具。
   > https://www.npmjs.com
+ npm概念
  - npm不需要额外安装，nvm在安装特定版本的node时就附带安装了
  - 每一个使用npm去管理包的项目，都需要有一个package.json文件，
  这个文件的作用就是用来描述和记录项目相关信息。
  - 用来安装项目中依赖的 package
  - 自身就是一个包，这个包是用来管理其它包
  - 包可以是一个提供API的包，也可以是一个工具包
  - 如果一个包提供的是API，一般我们将其安装到项目文件夹中
  - 如果一个包提供的是一个工具，安装到全局目录下。这样可以在任意目录下使用

+ 解决 npm 数据源不稳定
  + registry 选项
    - 作用：修改镜像源
    - npm config set registry https://registry.npm.taobao.org
    - 可以借助 [nrm](https://github.com/Pana/nrm) 管理
    - npm install nrm -g --registry=https://registry.npm.taobao.org
+ npm常用命令
     - npm init 初始化模块
     - npm install 安装模块
     - npm uninstall 卸载模块
     - npm update 更新模块
     - npm outdated 检查模块是否已经过时
     - npm ls 查看安装的模块
     - npm init 在项目中引导创建一个package.json文件
     - npm help 查看某条命令的详细帮助
     - npm root 查看包的安装路径
     - npm config 管理npm的配置路径
     - npm cache 管理模块的缓存
- yarn 基本使用：类比 npm 基本使用

### 自定义包

- 一系列相关模块组合到一起形成一个完整功能模块
- 多个模块可以形成包，不过要满足特定的规则才能形成规范的包  

##### 包的规范（约定）
- nodejs中用npm初始化来创建package.json
- package.json 必须在包的顶层目录下
- JavaScript 代码应该在lib目录下
- 文档应该在 doc 目录下
- 单元测试应该在 test 目录下

##### package.json字段分析

- name：包的名称，必须是唯一的，由小写英文字母、数字和下划线或中划线组成，不能包含空格
- description：包的简要说明
- main：指的是 包被载入时 默认加载的文件
- entry point 指的是包被载入时默认加载的文件
- version：符合语义化版本识别规范的版本字符串
- keywords：关键字数组，通常用于搜索
- dependencies：生产环境包的依赖，一个关联数组，由包的名称和版本号组成
- devDependencies：开发环境包的依赖，一个关联数组，由包的名称和版本号组成

#### npm包安装方式

- 本地安装
  - 在项目代码中使用某个模块
  - 如果一个包提供的是 API，一般我们将其安装到项目文件夹中，通过 require 使用
  - `npm install <package-name>`
- 全局安装
  - 一般工具包会全局安装
  - 如果一个包提供的是一个工具，一般将其安装在全局目录，这样可以在任意目录下使用
  - `npm install <package-name> --global`
  - less          用来编译 less 文件的
  - http-server   通过 node 启动一个 http 服务，默认将当前目录作为网站根目录
  + browser-sync 
    - 使用： browser-sync start -s . -f "*.html,*.css,*.js " 
    --nonotify
    - browser-sync start help打印start下的参数
    - -s . 服务器管理的路径
    - -f "*.html,*.css,*.js " 服务器管理的文件类型
    -  --nonotify 十分进行监控文件修改后浏览器页面更新
  - cli 工具 command line interface 工具

### Web 开发

+ 服务器模型，对比 Node.js 模型与传统服务端模型
  + node
    - 单线程，如果报错了，后续的用户就一直出于等待状态。
    - node本身就提供HTTP服务，在node中开一个服务，对某个端口进行监听，发现请求调用对应页面发回给用户。
    - node要做一些不耗时的操作，文件等操作都交给异步。
    - node底层实现还是多线程，比如异步的多个文件操作。
    - 在多核cpu的情况下，node可以通过多进程的方式来达到其它后台语言多线程的优势。
  + php
    - 多线程。
    - apache提供监听端口服务，发现是.html后缀的文件，直接返回给用户，发现是.php后缀的文件，把任务交给php，php处理后把页面返回给apache，apache再返回给用户
    -  apache -- 提供 HTTP 服务（能接收符合 HTTP 标准的请求，并返回符合 HTTP 标准的响应）
+ 线程概念
    - 程序再window上的表现为一个.exe的文件
    - 启动程序就是启动一个进行。
    - 线程是cpu最小调用单元
    + 在单核cpu情况下，多线程都是假的。
       - 线程是交给cpu执行，cpu执行是快速切换线程，本质上还是一个人做多个事。上下文切换消耗性能。
       - 就像一个人做多件事，node就是做完a任务再做b任务。
       - 而php是做a任务做到一部分，再去做b任务，做了一部分b任务，又回来做a任务。
       - 在a切换到b时，a的一部分准备被舍弃了，切换回来，又要把a的资源捡起来，这很消耗性能。
      
+  处理静态文件请求（实现静态网站服务器功能）
  - 由于 Node.js 没有内置静态文件请求处理功能，所以需要开发人员编码完成

### 知识点

+ 命令的本质
  - 找到一个可执行文件
  - 第一个空格以后全部都是提供给这个进程的参数
-   php<?php 文件中后面不要写封闭的?>，因为写了会把?>后面的回车当成空格解析，返回的文档会多一个空格。 



## Nodo 中的快速开发框架,中间件(7.29)

- 在 Node 开发领域最著名的快速开发框架肯定是 Express
- tj  connect express  koa
- Express 简介
  - Express 是一个非常简单的 Web Framework。就像我们在页面开发时用的 jQuery 一样，只是让我们的开发过程更加高效。
- 它的核心特性：**中间件机制**

### 中间件（管道）的概念

<!-- ![](media/middleware.png) -->

- 每一个环节就是一个中间件，每个中间件中都可以控制下个环节的工作
- 比如：接收到请求过后，先判断是不是静态文件请求，在处理动态文件请求
  - 对于流程性特别强的程序开发过程，中间件是最适合也是最灵活的方式。
    - Node 开发领域中第一个实现中间件机制的框架就是 Connect，Connect 就是 Express 的前身。

### Connect

> Connect is a middleware layer for Node.js
> Connect is a simple framework to glue together various "middleware" to handle requests.

```js
const url = require('url')
const path = require('path')

const connect = require('connect')

const app = connect()

// 第一个中间件处理静态文件请求
app.use((req, res, next) => {
  const urlObj = url.parse(req.url)
  const extname = path.extname(urlObj.pathname)
  const staticExt = ['.html', '.css', '.js']
  if (staticExt.includes(extname)) {
    res.setHeader('Content-Type', 'text/plain; charset=utf-8')
    res.end('静态请求')
    return
  }
  next()
})

// 第二个中间件处理动态请求
app.use((req, res, next) => {
  res.setHeader('Content-Type', 'text/plain; charset=utf-8')
  res.end('动态请求')
})

app.listen(3000)


```

### connect中间件框架

```js
const http = require('http')

/**
 * 中间件执行框架
 */
function Connect () {
  // 如果不是通过 new 的方式执行，则我们帮他 new
  if (!(this instanceof Connect)) return new Connect()
  this.middlewares = []
}

/**
 * 载入一个中间件
 * @param  {Function} middleware 中间件函数
 * @return {[type]}            [description]
 */
Connect.prototype.use = function (middleware) {
  this.middlewares.push(middleware)
}

/**
 * 启动一个 HTTP 服务并监听一个 指定的 端口
 * @return {[type]} [description]
 */
Connect.prototype.listen = function (...args) {
  /**
   * 请求时触发的请求处理函数（事件处理函数）
   * @param  {[type]} req [description]
   * @param  {[type]} res [description]
   * @return {[type]}     [description]
   */
  const handleRequest = (req, res) => {
    // this 指向 重点看的是调用时 而不是定义时
    // console.log(this) // 如果不去处理 就应该是 server
    let index = -1

    const next = () => {
      index++ // 执行下一个
      const current = this.middlewares[index]
      current && current(req, res, next)
    }

    // 起个头去调用第一个中间件函数
    next()
  }

  const server = http.createServer(handleRequest)
  // server.listen.apply(server, args)
  server.listen(...args)
}


module.exports = Connect
```

#### 知识点

+ new它是函数调用的一个修饰词
   - this创建一个新的空对象，作为这次调用函数的对象
   - return在执行完这个函数过后，把this作为返回值返回
+ nodemon包，自动更新node站点包   
   - nodemon nodejs文件更新页面更新，不用重启node
- del package.json  
- npm init --yes 直接初始化
- Node中不去区分请求的pathname，所有的请求都在 请求
处理函数中处理 
- Apache：创建一个http服务；php：开发动态网站

````js
  //  HTTP 协议中约定 如果响应报文中有 location 就会让客户端跳转
  // 301永久性跳转 302暂时性跳转
      request.statusCode = 302
      request.setHeader('Location', '/list')
      
   //接收数据事件
    request.on('data', chunk => {
      chunk.toString('utf8') //返回一个流，转化为字符串
    })
    .on('end', () => {})//数据接收完成事件

   //服务对象，监控异常
    server.on('error', err => {})
````


## Express入门(7.30)

- 一个基于connect的中间件框架，express 3.x 内置很多中间件，express 4.x 变成按需加载
> http://expressjs.com.cn/

- app.[METHOD]([PATH], [HANDLER])
  - app 是 express 的实例。
  - METHOD 是 HTTP 请求方法。
    - get(指定请求类型) use(支持所有类型，HANDLER可以是一个路由对象)
  - PATH 是服务器上的路径。
  - HANDLER 是在路由匹配时执行的函数。
- 处理静态文件请求  app.use(express.static('public'))
- express 的基础 api 同 connect 相同;express 在 connect 基础上做的改变
  - 增加了更为复杂的路由机制

####  异常处理

- 如何处理 404 响应？

  在 Express 中，404 响应不是错误的结果，所以错误处理程序中间件不会将其捕获。此行为是因为 404 响应只是表明缺少要执行的其他工作；换言之，Express 执行了所有中间件函数和路由，且发现它们都没有响应。您需要做的只是在堆栈的最底部（在其他所有函数之下）添加一个中间件函数来处理 404 响应：

  ```js
  app.use(function(req, res, next) {
    res.status(404).send('Sorry cant find that!')
  })
  ```
- 如何设置错误处理程序？

  错误处理中间件的定义方式与其他中间件基本相同，差别在于错误处理中间件有四个自变量而不是三个，专门具有特征符 (err, req, res, next)：

  ```js
  app.use(function(err, req, res, next) {
    console.error(err.stack)
    res.status(500).send('Something broke!')
  })
  ```
 #### 使用 Node + Express 开发动态网站步骤

1. 创建一个项目目录
2. 创建 package.json
3. 安装最基本的项目依赖：express
4. 设计最基本的编码结构
  1. 项目代码执行的入口文件 index.js
  2. 载入 express 模块，创建一个 app 对象
  3. 处理静态文件请求
  4. 对请求的路由做一个设计，有哪几种请求的地址，每一个请求处理函数
5. 业务逻辑编码过程


#### 模版引擎

handlebars模版
npm i hbs --save

在定义app对象的地方设置模版引擎

> http://expressjs.com/zh-cn/guide/error-handling.html

xtemplate模版
npm i xtpl xtemplate

#### 知识点
- npm i 包@版本
- sketch比ps设置图更快
- https://cva.cc/西游翻墙
- http://tongji.baidu.com/data/ 统计
- body-parser中间件：获取post提交的数据


## node项目登陆（8.2）

### music-server

1. npm init 初始化 package.json 文件
2. npm install 安装相关的项目依赖包（express xtpl xtemplate body-parser）
3. 搭建基本的 MVC 结构（指定项目约束）
4. 分别创建不同的 controller + model + view 去处理不同的业务（配置不同业务班子）
5. 具体的业务编码
6. 后续依赖包
   - mysql 数据库包
   - cookie-session 对session进行管理，自动生成key（cookie）给客户端
   - glob 获取某个文件夹下某个后缀的所有文件
   - xtemplate中可以拿到这个对象的值res.locals：res.locals={}

#### package.json解析

+  scripts 是用来定义预制命令
   - 可以通过 npm run <name> 执行这个命令 如果 name 为 test / start 可以省略 run
   - 还可以通过 pre<name> 和 post<name> 去定义这个命令之前和之后的命令
- devDependencies 中记录的是开发依赖（工具）
- dependencies 中记录的是我们代码的依赖 require 函数会用到的  
- npm i nodemon --save-dev 把工具包添加到当前项目中，好处是在新的地方会自动下载 
- MIT 最自由的协议，可以让任何人随意使用
```js
"scripts": {
    "test": "nodemon index.js",
    "prefoo": "echo prefoo",
    "foo": "echo hellokjjshdfjksjdfskdjfhkkkh",
    "postfoo": "echo postfoo"
  },
   "dependencies": {
    "body-parser": "^1.17.2",
  "devDependencies": {
    "nodemon": "^1.11.0"
  }
```

#### 知识点

+ 配置静态资源文件（放在管道最前面）
  - app.use(express.static('./public')) 
  - html中静态文件用绝对路径，网站根路径

+ Mockjs
  - mock 对于开发人员来说，是一种泛指所有客户端模拟数据的一种手段
  - mock 是一种客户端模拟数据的一种手段
+ 客户端请求处理
  1.校验
  2.持久化
  3.响应
- awesome + 名词 git上查询某个项目的清单
+ promise和委托（回掉函数）的区别：
  - promise是异步程序传递一个消息给同步然后同步去做这个事。
  - 委托就是把事情传递给异步，异步去执行。
  - 事件和promise很像，同步注册多个事件保存在一个第三方对象上，然后在异步时通知第三方对象，执行当前事件的所有处理程序。




## node文件上传处理，angular的demo(8.2)

+ MVC
    - 在MVC的结构中，请求最终都是要找到一个controller控制器下的action去处理(不再是一个页面，也不是一个单纯的文件)
    - 通过controller载入的中间件，只要这个控制器被执行，都会最先执行这个中间件
+ formidable（包）处理文件域参数
    - 当表单中有文件域的情况下，表单编码类型要修改为：enctype="multipart/form-data"

- gzip压缩静态文件服务器在传输过程中会进一步压缩，到浏览器在展开  

- script只有在type属性为text/javascript的时候，内部的代码才会作为javascript执行。

- 如果是让一个元素内部滚动加webkit-overflow-scrolling:touch，让元素有惯性滚动
+ node跨域jsonp响应处理
  - res.jsonp 方法会自动获取请求 QueryString 中键 为 callback 的值，
  - 将这个值作为回调函数名称，拼接一段 JS 代码

#### 知识点

- accept：html属性，可以限制上传文件类型
- angu版本1.6后才加跨域信任白名单
- axios 最流行的ajax库
- 有明确时间 有明确目标这就是项目
- audio site:w3school.com.cn 优化搜索关键词

























   
  



<file_sep>const path = require('path');
const fs = require('fs');


const obj={};
let num=0;
let tre='';
function filedg(pathstr,obj,wid) {
    
    const files = fs.readdirSync(pathstr);
   wid+=1;
    files.forEach(item => {
       
        if (fs.statSync(path.join(pathstr,item)).isFile()) {
            let len=fs.readFileSync(path.join(pathstr,item),'utf8').split('\n').length;
            obj[item] =len>0?len-1:0;
            num+=len;
        } else {

            obj[item]={};
            let r='';
            for(let i=wid;i--;){
             r+='   ';
            }
            tre+=r+'|'+'\n'+r+'---'+item+'\n'
            
            filedg(path.join(pathstr,item),obj[item],wid); 
        //    console.log(wid,r);

        }
    }) 
   
}
filedg(process.cwd(),obj,0);
console.log(obj,num);
console.log(tre);

<file_sep><!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>js+jq</title>
    <style>
        p {
            width: 100%;
        }
    </style>
</head>
<body>
<div><h2>第一天</h2>
    <h4>一、iconfont的使用：</h4>
    <p>作用：为了能够在页面上不再使用小图，将小图的内容直接用文本来表示以此来提前用户体验。</p>
    <ul>
        <li>百度搜索iconfont进入iconfont官网</li>
        <li>选择官网中的“图标库”--->“官方图标库”</li>
        <li>选择自己需要的字体库，点击进入，选择自己需要的字体，加入到购物车</li>
        <li>4）选择完成以后，点击购物车，选择下载代码</li>
        <li>5）进入下载的文件夹中，找到三个.html页面中的demo_fontclass.html，按照页面上的指示使用字体图标。</li>
    </ul>
    <h4>二丶其它属性的学习：</h4>
    <ul>
        <li>box-shadow: 0px 0px 10px #888888;</li>
        <li>设置阴影：左右平移 垂直平移 模糊程度 阴影颜色</li>
        <li>border-radius: 25px;</li>
        <li>placeholder:””;该属性设置默认文字</li>
        <li>w设置三角形：
            类名:after {
            content:””;
            height:0;
            width:0;
            line-height:0;
            border-width:5px;
            border-style: solid;
            border-color: transparent transparent blue;
        </li>
    </ul>
</div>
<div>
    <h2>第二天</h2>
    <ul>
        <li>块级元素水平集中：margin:0 auto等于 margin-left:auto+margin-right:auto</li>
    </ul>
</div>
<div>
    <h2>第三天</h2>
    <ul>
        <li>input系列之button按钮边框的宽度会占据内容部分，不会撑大盒子</li>
        <li>行内元素过长自动隐藏加省略号
            <p>
                a {
                width: 130px;
                overflow: hidden;
                text-overflow: ellipsis;
                white-space: nowrap;
                display: inline-block;
                }
            </p>
        </li>
    </ul>
</div>
<div>
    <h2>第四天复习js</h2>
    <h4>js的组成部分</h4>
    <ul>
        <li>1 ECMAScript js的基础语法规范</li>
        <li>2 DOM - 文档对象模型 document object model</li>
        <li>3 BOM - 浏览器对象模型 browser object model</li>
    </ul>
    <h4>浏览器的几个重要组成部分</h4>
    <ul>
        <li>1 用户界面</li>
        <li>2 浏览器引擎 - 中控</li>
        <li> 3 渲染引擎 -解析html和css代码 ：
            <ul>
                <li> 1 解析html代码：生成树模型</li>
                <li> 2 解析css代码：生成样式树</li>
                <li> 3 将样式树和树模型结合，生成渲染树（准备将页面效果显示出来）</li>
                <li> 4 根据渲染树上的每个部分，将真正的内容渲染在页面中</li>
            </ul>

        </li>
        <li>
            4 js解析器 ： 用于执行js代码
        </li>
    </ul>
    <h4>变量的作用：存储数据</h4>
    <p>数据的分类： 固定数据 临时数据;固定数据保存在硬盘中 临时数据保存在内存中</p>
    <h4>WebStorm快捷键</h4>
    <ul>
        <li>复制一行 ctrl + c</li>
        <li>复制一行到下一行 ctrl + d</li>
        <li>shift + enter回车 可以在下面快速创建一个新行</li>
        <li>格式化代码 ctrl + alt + l</li>
        <li>end键 可以跳到本行最后</li>
        <li>home键 可以跳到本行最前面</li>
        <li>ctrl表示按词跳跃</li>
        <li>shift表示选中</li>
    </ul>
    <h4>5大基本数据类型：</h4>
    <p>string number boolean null undefined</p>
    <p>typeof 用于对基本数据类型进行类型检测；用法两种：typeof 数据 typeof(数据)</p>
    <p>Infinity 无穷大（正无穷）;特殊值：NaN not a number 表示不是数值;
        js可以表示的最大值:Number.MAX_VALUE;js可以表示的最小正数:Number.MIN_VALUE
    </p>
    <p>null使用typeof结果为object，是不准确的，不能说明他是对象</p>
    <h4>类型转换</h4>
    <ul>
        <li>null 与undefined 没有对应的转换方式</li>
        <li>将其他类型转换为字符串类型：
            <ul>
                <li> 强制转换：数据.toString();String(数据)</li>
                <li>原因是null和undefined不具备toString方法</li>
                <li>隐式转换：使用任意数据类型 + 任意字符串，可以转换为字符串类型</li>

            </ul>
        </li>
        <li>将其他数据类型转换为数值类型：
            <ul>
                <li>强制转换:Number(数据)</li>
                <li>Number转换字符串时,如果字符串中含有字符，会转换失败，结果为NaN</li>
                <li>parseInt() 取出的数值为正数</li>
                <li>从左往右取，遇到不是数值为止，如果第一个字符就不是数值，转换失败，结果为NaN</li>
                <li>parseFloat(); 取出的数值可以包含小数;100.00这种数据的小数位没有意义，取出的值为100</li>
                <li>隐式转换：- * / %</li>
            </ul>
        </li>
        <li>将其他数据类型转换为boolean（布尔类型）
            <ul>
                <li>强制转换 Boolean(数据):能够转换为false的只有：false 0 "" NaN null undefined</li>
                <li>隐式转换 !!数据;console.log(!!null);</li>
            </ul>
        </li>

    </ul>

    <h4>运算符</h4>
    <p>== 相等比較，值等即可,!= 不相等 ，跟==对应; === 全等比較，比較值和类型,!== 不全等 ，跟===对应</p>
    <p>NaN 与任意类型判断==均为false</p>
    <p>isNaN(数据)如果数据值为NaN返回true，否则返回false</p>
    <ul>
        <li>当操作数不是bool值时：（短路操作）</li>
        <li>1 从左往右看</li>
        <li>2 如果操作数不是bool值，隐式转换</li>
        <li>3 哪个操作数可以决定式子结果，就返回这个源操作数</li>
        <li>4 如果第一个操作数无法决定结果，直接返回第二个操作数</li>
        <li>console.log(100 && 200);console.log(0 || 200);</li>
    </ul>
    <h4>结构语句,条件判断</h4>
    <p>switch 语句：如果条件中的基本值与某个case后的值全等，才会执行对应代码。</p>
    <p>使用switch进行范围的检测，需要在条件中书写true</p>
    <p>switch语句跟适用于单值的判断;if语句跟适用于范围的判断</p>
    <h5>优先级从高到低：</h5>
    <ul>
        <li>()</li>
        <li>一元运算符 ++ -- ! (+根据使用方式不同，优先级也不同)</li>
        <li>算数运算符 先* / % 后 + -</li>
        <li>比較 > < >= <= == === != !==</li>
        <li>逻辑运算符 && ||</li>
        <li>三元运算符 2>1?100:200;</li>
        <li>赋值运算符 = += -= *= /= %=</li>
    </ul>
    <h4>循环</h4>
    <ul>
        <li>for循环的适用场景：有规律有次数限制的重复执行代码</li>
        <li>使用while循环的适用场景：不确定循环次数重复执行代码</li>

    </ul>
    <h5>其他</h5>
    <p>当我们使用控制台调试时，注意的问题:1 不要在watch监视面板中监视可执行的代码;2 当我们需要查看一个变量的值时，最好使用watch，不要进行移入操作</p>
</div>
<div>
    <h2>第五天js循环和数组</h2>
    <h4>进制问题</h4>
    <ul>
        <li>2进制 逢2进1 01</li>
        <li>8进制 逢8进1 开头使用0进行表示 021</li>
        <li>16进制 逢16进1 0123456789 a b c d e f 0x21</li>
        <li>进制数表示当前是几进制，查看时从右往左看，左右侧的第一个位置位数为0</li>
        <li>当前位的数值 * 进制数^(位数-1)，多位之间进行结果相加</li>
        <li><p>
            把10进制转化为8进制：
            var num = 17;
            toString中转换一个数值参数表示要转换为几进制
            console.log(num.toString(8));8指的是转换后的进制
        </p></li>
        <li>
            <p>
                parseInt的第二个参数默认为10，表示当前数据为几进制，最终转换为10进制
                parseInt(数据,进制)
                var str = 21;
                console.log(parseInt(str, 8));8指的是转换后的进制
            </p>
        </li>
    </ul>
    <h4>循环</h4>
    <ul>
        <li> for的两种用法:
            1 获取一段有规律的数据
            2 有次数限制的重复执行代码
        </li>
        <li> 计算1-100之间的所有的质数
            <ul>
                <li> 质数：只能被1和自身整除</li>
                <li>能整除自己的数只存在与1-自身之间</li>
                <li>能整除自己的数<=Math.foolr(n/2)</li>
                <li>能整除自己的数<=Math.floor(Math.sqrt(n));</li>
            </ul>


        </li>
        <li>continue:结束本次循环，并且从下一次开始(顶部循环变量增减的位置)</li>
    </ul>
    <h4>数组</h4>
    <ul>
        <li>设置的值多余元素的个数:此种操作没有意义，不会添加新的数据;查看可能新增加的位置的值，值为undefined</li>
        <li>设置的值少于元素的个数:这时多余的元素会被删除</li>
        <li>清空数组:方式1:arr.length = 0;方式2：arr = [];console.log(arr);</li>
        <li>定义数组的两种方式(构造函数的方式，数组字面量的方式)
            var ary=new Array();var ary=[];
        </li>
        <li>数组.splice(索引,删除个数); //可以从数组中删除指定索引位置的数据</li>

    </ul>
    <h4>注意：</h4>
    <ul>
        <li> var arr = [1,2,3,4,5];
            console.log(arr);// 1,2,3,4,5
            arr[0] = 100;
            console.log(arr);// 100,2,3,4,5
        </li>
        <li>当上述代码在控制台打印时，结果与我们想的是一样的。</li>
        <li>但是如果将数组展开，这时控制台会重新读取对应数据的值，这时取值为新值</li>
    </ul>
</div>
<div>
    <h2>第六天数组，函数和作用域</h2>
    <h4>求数组类型的三种方式</h4>
    <ul>
        <li>Array.isArray(数组)，ie9一下浏览器不支持。ES5中提供的</li>
        <li>数组 instanceof Array，返回boolea值</li>
        <li>Object.prototype.toString.call(数组)，返回“[object Array]”字符串。推荐使用，可以访问任意的对象型数据的类型。</li>
        <li>二维数组及多位数组的应用：先判断数组的每个值，看是否有值为一个数组的，进行对应处理。（数组[i][j]）</li>
    </ul>
    <h4>基本数据类型和复杂数据类型的区别</h4>
    <ul>
        <li>保存数据的个数不同:基本类型保存一个值，复杂数据类型可以保存多个</li>
        <li>在内存中的保存方式不同：基本数据类型在内存单元中保存的是具体值，复杂数据类型在内存单元中保存的是具体值的指针</li>
        <li>基本数据类型和复杂数据类型在复制结果上不同:不同的原因是，复制操作复制的是变量指向的内存单元中的值</li>
        <li>null是基本数据类型;null代表了一个对象未初始化前的状态</li>
    </ul>
    <h4>函数</h4>
    <ul>
        <li>参数的使用场景： 当我们使用函数的时候，发现功能一定，内部的某些值不确定，可以给函数设置参数。</li>
        <li>参数的本质： 参数就相当于是函数内声明的一个变量</li>
        <li>形参的概念： 写在函数体中的参数称为形参，形参在使用时是没有值的，需要调用时传值。</li>
        <li>实参的概念： 形参值需要在使用时(调用函数),进行对应的传参，称为实参, 实参在函数调用的小括号中书写。</li>
        <li>如果函数没有设置返回值，默认为undefined</li>
        <li>函数调用表达式本身有两个作用：1 执行函数内部代码 2 本身代表了返回值</li>

    </ul>
    <h4>函数作用域及预解析变量和函数的提升方式</h4>
    <ul>
        <li>1 全局作用域：script内部并且不在任意函数内</li>
        <li>2 局部作用域：任意一个函数内部均为独立作用域,在js中有且仅有函数可以分割作用域(所以我们又将局部作用域称为函数作用域)</li>
        <li>3 全局变量：声明在全局作用域中的变量称为全局变量，在全局任意位置均可访问</li>
        <li>4 局部变量：声明在局部作用域中的变量称为局部变量，只能在声明这个变量的作用域内部使用</li>
        <li>5 查找当前作用域中的var变量声明语句，将变量声明语句提升到当前作用域顶端,赋值保留在原地</li>
        <li>6 查找当前作用域中的function函数声明语句,将函数体整体提升到当前作用域顶端,调用保留在原地</li>
        <li>js中函数不能重名，重名会使后者覆盖前者</li>
        <li>js中没有重载的概念</li>
    </ul>
    <h4>arguments模拟实现重载</h4>
    <ul>
        <li>arguments只能在函数内部使用</li>
        <li>arguments的数据结构具有索引，也具有长度，说明他是一个类数组结构(伪数组)伪数组就是没有数组方法的数组形式。</li>
        <li>实际上arguments就是当前这个函数的实参列表(内部保存了本次调用所传入的所有实参)</li>
        <li>可以利用arguments的length属性，进行判断，模拟函数重载的过程</li>

    </ul>
    <h4>函数补充</h4>
    <ul>
        <li>函数声明语句 function 函数名(){};2 函数表达式 ：将函数作为值保存在一个变量中进行使用,变量名就是函数名3.当一个函数没有名字，称为匿名函数</li>
        <li>函数表达式和函数声明语句的区别：预解析阶段的提升的规则不同，导致函数表达式无法在书写之前进行调用。函数表达式按照变量的声明方式，所以不会把方法体进行提升</li>
        <li>typeof 变量，可以判断变量是否是函数类型</li>
        <li>如果在操作参数时，如果实参值为复杂数据类型，在函数中直接修改时，实参也会对应改变</li>
        <li>函数没有声明但赋值了，预解析会默认声明到全局顶部</li>

    </ul>
    <h4>函数变量重名</h4>
    <ul>
        <li>有两个同名的函数，后者会覆盖前置。</li>
        <li>当函数和变量重名时，变量如果赋值了，因为变量是声明提升，而函数是整体提升，变量的值会覆盖函数；但变量没有赋值时，取函数值。因为空值不能覆盖有值的函数。</li>
    </ul>
</div>
<div>
    <h2>第七天对象和构造函数</h2>
    <p>将一个函数作为另一个函数的参数使用：整体功能一定，但是某些在不确定，传递一些数据参数即可；
        整体功能一定，但是内部某些子功能不确定，就需要将一些代码作为参数传递。再js中只有函数可以保存代码，传递的是函数体。
    </p>
    <ul>
        <li>函数也是一个数据类型，使用上和其他的数据没有本质区别</li>
        <li>可以使用匿名函数的方式，替代命名进行传参（减少函数名的占用）</li>
        <li>回调函数：把函数作为另一个函数的参数的行为称为回调函数。</li>
        <li>匿名函数：没有名字的函数。使用场景：
            1.保存再变量中作为函数表达式使用；
            2.作为参数传递使用（回调函数）
        </li>
        <li>
            自调用函数（逻辑执行函数）形式。执行方式：在书写的位置调用一次。
            作用：模拟块级作用域，减少全局变量的使用，提高性能，减少命名难度。
            语法：(function(){};)();
        </li>
        <li>块级作用域（了解）：js中没有块级作用域，有的只是函数内部的作用域；其他语言中，每个代码块的内部都是独立的作用域。</li>

    </ul>
    <h4>对象简介</h4>
    抽象和具体的关系：
    <ul>
        <li>抽象：生活中我们经常使用一些抽象的话来描述一些事物</li>
        <li>具体：当一个抽象的内容，内部所有特征确定后，变成了一个具体的事物</li>
        <li>抽象的作用：让我们在使用时可以预先知道一些事物所具有的特征，但是这些特征并不具体</li>
    </ul>
    <h4>对象的声明方式</h4>
    <ul>
        <li>var 对象名=new Object();创建一个空对象。</li>
        <li>给对象添加一些特征和行为：特征，称为对象的属性；行为，称为对象的方法。</li>
        <li>所有的方法都是函数类型，方法实际上也是属性，只不过保存的值为函数，所以起了一个特殊的名字。</li>
        <li>属性操作的两种方式：1.对象名.属性（已经确定使用的属性名）2.对象名["属性名"]（如果不确定属性名，可以通过变量获取）</li>
        <li></li>
    </ul>
    <h4>this的使用</h4>
    <ul>
        <li>this只有在方法中使用才有意义</li>
        <li>this在方法中指向函数的调用者</li>
        <li>好处，书写简便，可以将多个重复的方法合并为同一个对象。</li>
        <li>
            <strong>1 外面的this为new更改过指向后的对象
                2 内部的this指向当前函数的调用者(这个函数属于谁)</strong><br>
            function create(ageIpt) {

            //我们需要添加属性和方法，问题是属性和方法给谁设置？
            //在这里可以使用this进行属性和方法的设置
            this.name = "张三";
            this.age = ageIpt;

            //添加一个方法
            this.sayHi = function () {
            //方法内外的this的用法不同

            console.log("你好我是" + this.age);
            };
            }
        </li>
    </ul>
    <h4>构造函数创建对象</h4>
    <ul>
        <li>这种创建多个对象的形式我们称为构造函数方式创建</li>
        <li>使用函数创建对象时，由于多个对象的方法是相同的，会重复占用空间，可以使用命名函数的方式</li>
        <li>new关键字做的事情：
            1.创建一个对象。2.将当前函数内的this指向更改为使用new创建的对象。3.返回这个对象。
        </li>
        <li>使用new调用这个函数我们称为构造函数</li>
        <li>1.构造函数的命名，首字母大写（推荐，强烈推荐）</li>
        <li>2.调用构造函数时要使用new</li>
        <li>3.构造函数的作用：创建对象</li>
        <li>4.在构造函数中只需要做一件事，给this添加属性和方法</li>
        <li>在构造函数中不要写reture：1.reture基本类型值：会被忽略掉。2.reture
            复杂类型值，这是这个复杂数据类型会被返回，原有的对象会被替换。
        </li>
    </ul>
    <P>
        构造函数优化：1.使用一个对象，保存所有的函数。节约了名字和内存空间，但是还占有一个名字2.prototype属性在构造函数完成后，构造函数添加匿名函数。

    </P>
    <h4>prototype属性</h4>
    <ul>
        <li>prototype这个属性就是一个对象，作用为，放置公用方法</li>
        <li> Create.prototype.sayTianQi = function () {
            console.log("今天天气不错");
            };
        </li>
        <li>每个对象都具有一个属性 __proto__;这个属性与创建这个对象的构造函数的prototype是相同的值</li>
        <li>当我们使用一个对象的属性(方法)时,首先会查找这个对象本身，如果有，使用 ;如果没有，找这个对象的__proto__属性，就相当于查找Create.prototype</li>
        <li>这个对象叫做原型对象</li>
    </ul>
    <h4>补充说明</h4>
    <ul>
        <li>window浏览器环境下js的顶级对象，在页面其他位置使用this为window</li>

    </ul>
</div>


<div>
    <h2>第八天对象和DOM</h2>
    <h4>对象简介</h4>
    <ul>
        <li>在js中对象是用来描述一个具体事物的语法</li>
        <li>构造函数和抽象对应，对象和具体对应。</li>
        <li>1 构造函数创建 var obj = new Object(); var obj = new Create();</li>
        <li>2 字面量形式：(键值对集合的形式) 键 - 属性名 值 - 属性值</li>
    </ul>
    <h4>对象和数组的区别</h4>
    <ul>
        <li>对象是一个复杂数据类型：对象中可以保存多个数据；数组同样是复杂数据类型</li>
        <li>区别在于：数组是一种按照索引有序的数据存储方式，对象按照属性名保存是一种无序的数据存储方式</li>
        <li>由于对象是无序的数据存储方式，在遍历过程中就不能使用普通的for循环。</li>
        <li>使用for in循环遍历对象</li>
        <li>对象的属性不存在时，如果进行访问值为undefined(对象.属性)</li>
    </ul>
    <h4>json简介</h4>
    <p>通常情况下，通过前后端进行交互使用的数据称为json;
        JSON的数据形式和对象的字面量很像，区别在于属性名加了双引号（必须是双引号）
    </p>
    <h4>面向对象简介</h4>
    <ul>
        <li>面向对象：面向对象是一种思想，关注于对象</li>
        <li>面向过程：专注于过程</li>
        <li>面向对象的写法：会具体的实现一个功能的每个部分</li>
        <li>面向对象思想实际上就是：拿来主义（拿过来用即可）</li>
    </ul>
    <h4>DOM</h4>
    <ul>
        <li>DOM- document object model 文档对象模型</li>
        <li>DOM提供一些方式，用来操作页面的元素。（在DOM中标签称为元素-页面元素）</li>
        <li>document是一个对象，并且在DOM中的顶级对象</li>
        <li>document.getElementById("id")传入一个字符串类型的标签id名，该方法获取返回该标签，取不到值返回null</li>
        <li>window.onload=函数;人口函数：在人口函数中书写的代码会在页面完全加载后（页面结构，图片，文件）执行</li>
        <li>每个标签都具有style属性，用于获取样式。通过：标签.style
            .具体的样式名；通过js设置的样式，最终在标签的行内生效（样式的权重很高）
        </li>
        <li>操作文本的属性：标签.innerHTML属性；可以获取修改标签内部的文本+标签</li>
        <li>防止覆盖：使用innerHTML+=的方式可以避免原内容被覆盖的问题</li>
        <li>新问题：即便使用+=可以解决覆盖，如果原有的内部标签具有了事件，这时由于新旧标签不同，这个事件会失效</li>
        <li>根据标签名获取元素：document.getElementsByTagName("标签类型")；
            不支持数组方法，但是有长度有索引，是一个伪数组形式。
        </li>
        <li>标签行内自带的（js自己支持的）属性，可以通过标签.属性名的方式进行直接操作</li>
        <li>注意：直接通过id.innerHHTML，浏览器会自动帮我们获取并使用，但这种写法不规范，容易出其他错误</li>
        <li>css的属性在js中，例如 background-color，在js中操作时需要修改为驼峰命名法backgroundColor (去掉横线，后半部分首字母大写)</li>
    </ul>
    <h4>事件</h4>
    <ul>
        <li>捕捉用户的操作称为事件；</li>
        <li>事件源：触发事件的元素（被动）</li>
        <li>事件类型：触发了什么事件</li>
        <li>事件处理程序：事件触发后，相应要执行的效果（函数结构）</li>
        <li>事件的语法格式：事件源.事件类型=function(){};</li>
        <li>onclick用户点击事件</li>
    </ul>
</div>
<div>
    <h2>第九天</h2>
    <h4>src和href的区别</h4>
    <ul>
        <li>href作为链接到外部资源直接使用</li>
        <li>src会将获取到的值，替换标签内的内容，进行显示（使用）</li>
        <li>给a标签注册点击事件，如果不想进行跳转，可以在自定义事件结束位置书写 return false;</li>
    </ul>
    <h4>在循环中添加事件时事件中使用循环变量的两个方法</h4>
    <ul>
        <li>采用自调用函数，在自调用函数中声明一个变量，存储循环变量。（会创建多个自调用函数，不推荐）</li>
        <li>使用标签的自定义属性，把循环标量存储到标签中。标签对象.自定义属性名=循环变量；</li>
    </ul>
    <h4>标签行内的类名className</h4>
    <ul>
        <li>类名的好处：使用类名可以让样式操作更加简便，利于css和js代码的分离</li>
        <li>取消类名和加入新类名来操作样式</li>
        <li>设置类名时，由于js设置的样式显示在行内，权重较高，类名无法对其进行覆盖，需要添加!important</li>
    </ul>
    <h4>事件</h4>
    <Ul>
        <li>移入事件 onmouseover;移出事件 onmouseout</li>
        <li>字符串.replace("匹配项","替换值");字符串的不可变性:所有的字符串方法，均会在操作后，返回修改后的结果，而无法修改原字符串</li>
        <li>输入框焦点事件：onfocus（获取焦点）和onblur（失去焦点）</li>
        <li>标签属性：disabled，checked，selected值在js中为Boolean；可以使用非bool值，会隐式转换后，根据转换结果进行操作</li>
        <li>纯文本时innerText性能高于innerHTML</li>
    </Ul>
    <h5>表单的基本操作</h5>
    <ul>
        <li>文本操作：通常使用value属性</li>
        <li>textarea 可以使用value和innerHTML</li>
        <li>select中的option：只能使用innerHTML，具有value属性但是不是文本操作的作用</li>
    </ul>
    <h4>Tab切换的两种方法</h4>
    <p>针对法：定义一个变量赋默认值0，在循环中每个子元素的注册事件中，先清掉当前变量对应的子元素选择样式，第二步给当前点击
        的元素加选择样式，第三个给变量赋值为当前子元素的索引（通过自定义属性给子标签加索引）</p>
    <p>排他法：先通过循环清除所有子元素的样式，然后在给当前子元素加元素。优点：实现简单。缺点：性能差</p>
</div>
<div>
    <h2>第十天dom获取文档标签及兼容性</h2>
    <p>能力检测：检测浏览器行不行（检测支持哪一个功能）;使用特定的某个属性和方法，在执行时查看执行的结果，如果可以，就使用该功能，如果不行，使用其他对应的功能；</p>
    <h4>innerText和textContext（都是操作纯文本）</h4>
    <ul>
        <li>innerText属性是ie提出的，其他浏览器兼容性不好</li>
        <li>textContext其他浏览器支持，但ie9下不支持</li>
        <li>如果浏览器不识别这个属性，返回undefined</li>
    </ul>
    <h4>获取计算后的样式</h4>
    <ul>
        <li>getComputedStyle(要进行样式获取的标签,null) 获取计算后的样式:可以获取各种位置设置的样式值</li>
        <li>第二个位置表示某个标签的伪元素，通常传入null或者0或者false；使用getComputedStyle获取的结果是所有样式的集合(对象形式)</li>
        <li>ie低版本(ie9以下)不支持这个标准方法getComputedStyle()</li>
        <li>ie使用 标签.currentStyle 获取结果与getComputedStyle()的结果相同</li>
    </ul>
    <h4>getElementsByClassName</h4>
    <ul>
        <li>ie9以下不支持</li>
        <li>能力检查后自己实现</li>
        <ul>
            <li>document.body.getElementsByTagName("*")可以通过通配符的方式获取所有的标签进行判断</li>
            <li>按照空格把获取到每个标签的类名进行split，和传入的类名进行对吧，一样的加入一个新数组</li>
            <li>优化：tagElement（标签对象）||document.body按照断路的方式，如果传入了标签对象，则获取这个标签下所有带有该类名的标签</li>
        </ul>
    </ul>
    <p>标签对象.style.cssText= "width:100px;height:100px;";使用一个样式属性 cssText 可以一次给标签添加多个样式；cssText用于添加css形式的样式值</p>
    <h4>节点树</h4>
    <ul>
        <li>html生成的这个树称为节点树（树模型）;树模型上有多个不同的部分，每个部分我们称为节点</li>
        <li>节点的分类：元素节点 - 标签;文本节点 - 文本;属性节点 - 属性;注释节点 - 注释（不会进行操作，不常用）</li>
        <li>document - 节点根</li>
        <li>节点树带给我们的好处；根据节点之间的上下级(父子关系)、同级(兄弟)，提供了一套访问的方式</li>
    </ul>
    <h4>节点访问方式</h4>
    <ul>
        <li>通过节点访问关系获取的结果，如果是数组形式的数据，全都是伪数组。</li>
        <li>会获取文本节点的方法，其中文本包括标签正常的空格换行</li>
        <li>父子节点访问方法
            <ul>
                <li>想要通过一个节点，访问他的上级节点(父节点、父元素)语法：节点.parentNode;注意：任意节点的父节点，永远是元素节点(标签)，不可能是其他类型的节点。</li>
            </ul>
        </li>


        <li>子节点访问
            <ul>
                <li>获取所有子节点:节点.childNodes;获取的值，包含标签内部所有的子节点（元素节点，文本节点（换行和空格））</li>
                <li>获取所有的子元素节点:节点.children;获取的值为标签内部的所有子标签，不含文本节点;children不是一个标准属性，但是由于特性非常好用，导致所有的浏览器均支持此属性</li>
            </ul>
        </li>
        <li>
            获取子元素第一个或最后一个元素子节点
            <ul>
                <li>节点.firstElementChild和节点.lastElementChild，不会获取到文本节点，但ie9下不支持</li>
                <li>节点.firstChild和节点.lastChild，会获取文本节点，但没有兼容性问题</li>
            </ul>
        </li>
        <li>
            获取兄弟节点前一个或者后一个
            <ul>
                <li>节点.previousSibling和.nextSibling 会获取到空格和换行</li>
                <li>节点.previousElementSibling和节点.nextElementSibling 不会获取文本节点，但ie低版本不支持</li>
                <li>如果通过同级访问关系获取不到元素,结果为null</li>
                <li>封装节点.previousSibling和.nextSibling，遇到空格和换行（判断节点类型）直接向外跳一级查元素和为null直接返回，可以得到只获取前后元素的方法（第十二天视频进行了封装）</li>
            </ul>
        </li>
    </ul>
    <h4>节点位置移动方式(移动节点的方式:移动节点，类似与剪切操作;)</h4>
    <ul>
        <li>父节点.appendChild(要进行添加的节点) 追加子节点;添加位置为父节点的所有子节点最后。</li>
        <li>父节点.insertBefore(要插入的元素,参考的子节点) 插入子节点;添加位置为指定的子节点前面。</li>
        <li>insertBefore的第二个参数不传也是可以使用的（不是完全不传，而是传一个没有用的值）;可以传的值为null和undefined</li>
        <li>这个方法的规范中指出两个参数均为必须传入的参数，所以这种方式使用时需要谨慎对待(因为官方没有明确说明，以后可能会改)</li>
        <li>如果值为null和undefined时，传入的位置为最后。</li>
    </ul>
    <h4>节点访问伪数组长度死循环问题</h4>
    <p>在节点访问关系中，获取的数组结构数据均为伪数组;这些伪数组结构均为动态的;我们在操作时，每次向box中添加一个新的标签时，不要使用box.children的length作为判断添加
        问题是，由于伪数组结构是动态的数组形式，导致死循环的问题;如果想要采用伪数组的元素个数作为循环的执行次数使用，需要先进行保存到一个变量中，然后这个变量做循环参数。
    </p>
</div>
<div>
    <h2>第十一天dom,动态创建删除元素，innerHTML性能，onkeyup事件</h2>
    <h4>动态创建元素几种方式</h4>
    <ul>
        <li>动态创建元素的作用：
            <ul>
                <li>1.减少流量消耗</li>
                <li>2.提高网页的加载速度</li>
                <li>3.增强网站的可维护性</li>
             </ul>
        </li>
        <li>document.write();
            <ul>
                <li>插入位置，为body的最底部</li>
                <li>问题1 ：不能在某个指定的标签内部进行结构创建</li>
                <li>问题2： 如果在页面加载后(事件中)使用document.write()会导致原有的页面被覆盖</li>
                <li>页面在渲染时:1 默认加载(第一次加载页面)：称为一次输出流 （将效果输出到页面上）2 document.write() 如果在页面加载后使用，会开启一个新的输出流（导致原有输出流的内容被清除）</li>
                <li>解决方式：不要在页面加载后使用document.write();</li>
            </ul>
        </li>
        <li>标签.innerHTML
            <ul>
                <li>好处：灵活，可以给任意的标签内部进行结构创建;坏处：性能极差</li>
                <li>只需要减少innerHTML的使用次数即可</li>
                <li>拼接字符串老式的使用方式是使用数组然后.join("")：因为低版本浏览器对字符串操作的优化不好</li>
                <li>+=innerHTML操作会把元素注册的事件干掉</li>

            </ul>
        </li>
        <li>document.createElement("标签名")  可以进行标签的创建
            <ul>
                <li>使用document.createElement() 创建的元素，不存在于页面中</li>
                <li>如果想要显示，需要使用移动节点的方式，将创建的元素添加到页面中的某个位置即可</li>

            </ul>
        </li>
        <li>根据下面的各种例子总结的规律：
            <ul>
                <li> 1 当结构十分复杂时，使用innerHTML非常方便，直接拼接即可.</li>
                <li>2 当我们需要向一个标签添加新元素时，如果这个标签内部已经具有了其他的内容，这时使用document.createElement()可以不影响原始的标签</li>
            </ul>
           </li>

    </ul>
    <h4>事件，元素删除及字符串判断位置</h4>
    <ul>
        <li>父节点.removeChild(要删除的子节点);</li>
        <li>on key 键 up起来;onkeyup 按键按下去，抬起来的一瞬间，触发事件</li>
        <li>字符串方法indexOf;  返回从左往右第一次找到的指定字符串的索引值</li>
    </ul>
    <h3>查看此代码执行的时间  console.time("key") 与console.timeEnd("key")前后key一致</h3>
    <p>最好不要使用父级作用域中的变量;通过节点之间的访问关系，可以防止父作用域执行后不进行释放的问题。</p>
    <p>/和"++"一样可以对字符串的换行起作用</p>
</div>
<div>
    <h2>第十二天节点，bom</h2>
    <h4>重排和重绘</h4>
    <ul>
        <li>
            <ul>
                <li>1 当页面加载生成渲染树(节点树和样式树合并的产物)后;按照渲染树上的每一个部分，将页面显示出来</li>
                <li>2 排列：根据某个标签的宽高，定位值，浮动，计算出标签要显示的位置</li>
                <li>3 绘制：显示内部的内容，图片，颜色，文本</li>
            </ul>
        </li>
        <li>当用户对页面进行操作时(js操作)，可能会引发重新排列(重排、回流)和重新绘制(重绘)</li>
        <ul>
            <li>重排和重绘均会引发性能问题：重排的性能消耗要高于重绘</li>
            <li>重排的出现位置，决定了他消耗的性能(尽量减少一个元素在进行操作时所可以影响的元素个数)</li>
            <li>重排是必然引发重绘，但是重绘是可以单独触发的。</li>
            <li>尽量要减少操作所引发的重排次数</li>
        </ul>
        <li>document.createDocumentFragment();作用：当我们将df放入到页面中时，自己不会像标签一样一同放入，而是放入内部的元素</li>
    </ul>
    <h4>节点克隆与替换</h4>
    <ul>
        <li>节点.cloneNode();</li>
        <ul>
            <li>1 可以传入参数，如果不传，默认为false，表示浅复制（只复制标签，不复制内部的内容）;如果参数值为true，表示深复制(复制标签以及内部的内容)</li>
            <li>2 克隆节点后，标签相同，但是不会具有内部的事件(等等的属性都没有);所有在标签结构中看不到的属性均无法克隆</li>
            <li>3 要克隆的标签不要添加id，添加id以后，id名会重复</li>
            <li></li>
        </ul>
        <li>标签.replaceChild(要替换成哪个标签,要替换的子元素);返回值为被替换掉的标签</li>
    </ul>
    <h4>节点属性</h4>
    <ul>
        <li>节点是对页面中所有内容的统称(节点树中的每个部分)</li>
        <li>
            <ul>
                <li>节点.nodeType  用于查看每个节点的节点类型：元素节点  1；文本节点  3；属性节点  2；document  9；documentFragment 11</li>
                <li>nodeName  节点名；元素节点的nodeName属性为标签名,在html中是大写形式,在xhtml中是小写形式</li>
                <li>nodeValue 节点值；元素节点没有节点值，访问时值为null</li>
           </ul>
        </li>
    </ul>
    <h4>标签的属性</h4>
    <ul>
        <li>标签自带的行内属性可以通过 标签.属性名直接操作;标签行内的自定义属性操作：使用.属性名访问不到
            <ul>
                <li>1 属性获取:标签.getAttribute("属性名")</li>
                <li>2 属性设置：标签.setAttribute("属性名")</li>
                <li>3 属性移除：标签.removeAttribute("属性名")</li>
                <li></li>
                <li></li>
            </ul>
        </li>

        <li>属性节点的获取方式：var node= 标签.getAttributeNode("属性名")
            <ul>
                <li>node.nodeType//2</li>
                <li>node.nodeName//id 属性名</li>
                <li>node.nodeValue//属性值，取值与getAttribute取值相同</li>
             </ul>
        </li>

    </ul>
    <h4>bom</h4>
    <ul>
        <li>BOM - browser object model 浏览器对象模型;作用：将浏览器内部的功能作为对象形式进行使用;BOM中的顶级对象 - window</li>
        <li>所有的全局变量均为window的属性;window的属性和方法均不需要(可以忽略)加window;使用window的方式可以直接访问全局变量的值</li>
        <li>location也是window的属性。同样也是一个对象形式，内部具有许多的属性和方法
            <ul>
                <li>location具有一个属性href，会跳转到指定的页面</li>
                <li>location.assign(地址); 同样也是跳转功能;和href功能基本一样</li>
                <li>location.replace(地址); 跳转，但是无法进行返回(无法后退)</li>
                <li>location.reload(); 刷新</li>
                <li>
                    <ul>
                        <li>window.location.hash;//锚点</li>
                        <li>window.location.host;//主机</li>
                        <li>window.location.hostname;//域名</li>
                        <li>window.location.pathname;//当前页面的路径</li>
                        <li>window.location.port;//端口 80</li>
                        <li>window.location.protocol;//协议</li>
                        <li>window.location.search</li>
                    </ul>
                </li>
            </ul>
        </li>
        <li>window.navigator.platform//平台，可以检测当前运行的系统是什么;window.navigator.userAgent//用户代理字符串  简称UA</li>
        <li>history.go(1); history.forward();//前进history.go(-1);history.back();//后退</li>
        <li>window.open()//open()方法: 参数1 打开地址 参数2 打开位置 _blank _self 参数3 可以设置新窗口的样式
        <ul>
            <li>close方法,用于关闭窗口</li>
            <li>moveTo(w,h),moveBy(w,h)修改窗口大小</li>
            <li>resizeTo(x,y)，resizeBy(x,y)移动窗口位置</li>
        </ul>
        </li>

    </ul>
</div>
<div>
    <h2>第十三天</h2>
    <p> 内置对象:类似与电脑的内置软件，具有一些自带的功能；
    由于每个方法使用的数据不同，又形成了不同的集合（对象），称为内置对象
    </p>
    <h4>内置对象数组的方法</h4>
    <ul>
        <li>数组.push(参数1,参数2,..........);
            <ul>
                <li>表示将传入的参数依次添加到数组的最后</li>
                <li>可以修改原数组，返回值为修改后数组的新长度</li>

            </ul>
        </li>
        <li>数组.pop():从数组的最后删除一个元素，并且返回。修改原数组</li>
        <li>数组.unshift(参数1,参数2,..........)从数组最前面进行添加;按照传参的顺序，将数据添加到数组最前面;返回数组的新长度</li>
        <li>数组.shift();从数组最前面删除一个元素</li>
        <li>数组.concat()
            <ul>
                <li>用于进行数组的连接,如果参数为数组形式，不会将整体放入，而是将传入的数组中每个元素进行放入</li>
                <li>不会修改原数组，而是返回一个结果数组</li>
                <li>利用返回新数组的特性，可以进行数组的复制</li>
             </ul>
        </li>
        <li>slice()  拷贝
            <ul>
                <li>数组.slice(start,end);</li>
                <li>作用为，拷贝数组中指定的数据，从索引值start开始拷贝到索引值end位置，不含end位置的值</li>
                <li>不会修改原数组，返回值为拷贝的内容</li>
            </ul>
        </li>
        <li>splice()  截取
            <ul>
                <li>数组.splice(start,len)  start表示截取的起始位置索引值，len表示截取的个数</li>
                <li>修改原数组，返回值为截取到的元素，数组形式返回</li>
                <li>添加数据，如果传入的参数多于2个,将后面的这个参数添加 到截取的位置上(个数不需要对应)</li>
                <li>splice的作用：可以删除数组中的指定元素;</li>
            </ul>
        </li>
        <li>数组.reverse();翻转数组:可以修改原数组，返回值与原数组是同一个数组，所以没有必要接收</li>
        <li>数组.sort();
            <ul>
                <li>可以对字符串数组进行排序，默认为按照a-z的顺序排序，如果数组的元素具有多个字符，按照首字母排序</li>
                <li>可以对数值数组进行排序,用冒泡排序模拟写出sort传函数的排序方式</li>

            </ul>
        </li>
        <li>indexOf():返回值为，从左往右找到的第一个满足条件的元素的索引值(只能找到一个);如果没有找到指定的元素值，返回-1</li>
        <li>lastIndexOf:使用方式与indexOf相同，只不过查找的起始位置为从后往前;数组的indexOf和lastIndexOf方法在ie9以下不支持</li>

    </ul>
    <h4>Date对象</h4>
    <ul>
        <li>Date对象的作用：表示时间;</li>
        <li>Date对象的创建方式：var date = new Date();//构造函数创建方式，给当前的时间创建了一个对象形式</li>
        <li>传参可以创建某一个特定时间的对象：var date = new Date("2015-1-1");</li>
        <li>如果参数是数值类型，月份从0开始:var date = new Date(2015, 11, 1);</li>
        <li>转换方式： 会将当前的日期转换为毫秒形式
            <ul>
                <li>var date = new Date();console.log(date.getTime());console.log(+new Date());</li>
                <li>console.log(Date.parse("2015-1-1"));</li>
                <li>var date = new Date();console.log(date.valueOf());</li>
                <li>Date.now();</li>
            </ul>
        </li>
        <li>
            Date的内置方法
            <ul>
                <li>console.log(date.getFullYear());//获取年份</li>
                <li>console.log(date.getMonth() + 1);//获取月份，月份从0开始</li>
                <li>console.log(date.getDate());//获取日</li>
                <li>console.log(date.getDay());//星期也是从0开始，0表示周日</li>
                <li>console.log(date.getHours());//获取小时</li>
                <li>console.log(date.getHours());//获取小时</li>
                <li>console.log(date.getMinutes());//获取分钟</li>
                <li>console.log(date.getSeconds());//获取秒</li>
                <li>console.log(date.getMilliseconds());//获取毫秒  0-999</li>

            </ul>
        </li>
    </ul>
    <h4>字符串对象</h4>
    <ul>
        <li>字符串就是一个基本类型数据;由于字符串是一个非常常用的数据类型，许多基本的操作，如果每次都要书写代码实现非常的麻烦;</li>
        <li>js会在每次使用字符串的属性和方法时，给我们创建一个与该字符串数据值相同的字符串对象;当属性使用完毕后，这个对象会被立刻清除</li>
        <li>这种对象我们称为 基本包装类型对象 。</li>
        <li>length表示字符串的字符个数;按照索引访问字符,由于字符串的不可变性，操作时只能进行访问，而无法修改。字符串.charAt(索引值) 使用结果与[]的方式相同</li>
        <li>字符串.concat();作用为连接字符串;这个方法通常不用，一般使用+操作，更简便</li>
        <li>字符串.slice(start,end);slice方法可以传入负数的参数，表示从后面数第几个</li>
        <li>字符串.substr(start,len)  len表示个数</li>
        <li>字符串.substringe(start,end);不支持负数的参数，如果传入了负数，会认为是0;如果参数2的值小于了参数1，自动交换位置</li>
        <li>字符串大小写转换：小转大/字符串.toUpperCase()；大转小/字符串.toLowerCase()</li>
        <li>字符串.indexOf("要检索的字符串",起始位置索引值);字符串的index系列方法没有兼容性问题</li>
        <li>语法:str.replace("要替换的字符串","替换为什么字符串")作用为从左往右，替换掉找到的第一个指定字符串(可以循环替换全部)</li>
        <li>字符串.split("用于分割的字符串")  返回分割后的数据，数组形式</li>
        <li>字符串.trim()去除字符串两端的空格</li>

    </ul>
    <h4>两种定时器</h4>
    <ul>
        <li>timeout定时器:延时执行;设置：setTimeout(执行的代码,延时时间)  时间为毫秒单位。返回值为当前定时器的唯一标识;clearTimeout(定时器标识id)</li>
        <li>interval:每间隔一段时间重复执行代码;设置：setInterval(执行的代码,执行间隔时间)  时间为毫秒单位。;返回值为当前定时器的唯一标识;清除方式: clearInterval(定时器id);</li>
        <li>问题是：由于异步任务不是时间到达立刻执行，而是时间到达后将任务代码添加到主线程中进行排队；如果当前的同步任务列表中的代码执行时间很长，可能会导致定时器执行时间不准确</li>
        <li>当定时器内部的代码停止执行后，记得清除定时器</li>
    </ul>
    <h3>单线程和多线程丶同步和异步</h3>
    <ul>
        <li>单线程和多线程：
            <ul>
                <li>单线程就是做事情的人只有一个。</li>
                <li>js是单线程的语言：如果设置为多线程，会导致dom等关键操作出现问题</li>
                <li>h5以后js引入了多线程的概念，但是只能有一个主线程，其他线程无法进行DOM等核心操作，可以进行其他的细节处理，提高代码执行速度</li>
            </ul>
        </li>
        <li>同步和异步：
            <ul>
                <li>单线程同步：一个人只能按照顺序一次做一件事情。</li>
                <li>同步的问题：由于有些任务比較耗时，或者执行的时间不确定，这时无法使用同步任务（会导致后面的功能阻塞）</li>
                <li>异步任务：当某些任务比較耗时时，或者执行的时间不确定时，这些任务不会添加到主线程中进行执行，而是放到一个任务队列中</li>
                <li>异步任务的执行总是晚于同步任务；当异步任务可以执行时无法插入到已经存在的任务之间，只能排队到当前所有任务的最后等待执行</li>
                <li>js中的常见异步任务：定时器，事件</li>
            </ul>
        </li>

    </ul>
</div>
<div>
    <h2>第十四天</h2>
    <h4>旋转属性：/*旋转角度为顺时针*/ transform: rotate(30deg);</h4>
    <h4>简单运动</h4>
    <ul>
        <li>/使用标签.offsetLeft 用于获取标签距离定位父盒子的左侧距离 //获取结果是数值类型,并且只读(无法进行修改)</li>
        <li>offsetLeft的取值会取到真正应用的一个值，会进行四舍五入:每个像素点同时只能显示一个颜色，从根本上来说设置一个元素的样式为宽度20.3，根本没有意义</li>
        <li>计算方式为：当前元素的左边框左侧，到定位父盒子的左边框右侧(边框到边框之间的距离)</li>

        <li>标签.offsetWidth 可以获取元素的真实宽度(border + padding + width)(除了margin以外的所有值的宽度)获取结果是数值类型,只读（不能设置）</li>
        <li>1 在定时器内部先获取到元素当前所在位置的left值（可以避免默认left值变动和后期操作更改left的问题）</li>
        <li>2 简单运动公式  当前位置 = 当前位置 + 步长</li>
        <li>3 将新的位置设置给box的left，进行运动</li>
        <li>4.到达位置清除定时器</li>
        <li>多个盒子抖动:最终的修改是，将timer不作为全局变量或者局部变量，而是作为某个标签的一个自定义属性。</li>
    </ul>
    <h4>网络加载的速度对页面图片的尺寸获取有影响(这种情况不可控)。</h4>
    <ul>
        <li>入口函数：当页面完全加载后执行代码</li>
        <li>当我们需要使用某个图片的尺寸时</li>
        <li>方式1：可以获取可视区域的宽度</li>
        <li>方式2：有时我们的页面结构是动态创建的,会根据接受的数据进行结构创建，数据中会保存图片等内容的尺寸</li>
    </ul>
    <h4>列子</h4>
    <ul>
        <li>简单轮播图</li>
        <li>左右焦点图</li>
        <li>无缝滚动原理</li>
        <li>机械表</li>
     </ul>
</div>
<div><h2>第十五天加速滚动封装，轮播图和手风琴</h2>
    <p> //事件传递的默认方式，称为事件冒泡
        //当前元素触发事件后，自己的事件会执行，执行后会向父元素进行传递，如果父元素具有相同类型的事件，也会被触发</p>

</div>
<div>
    <h2>第十六天</h2>
    <h4>offset系列属性</h4>
    <ul>
        <li>offsetLeft:用于获取当前元素到定位父盒子的左侧距离  (边框到边框)</li>
        <li>offsetTop:用于获取当前元素到定位父盒子的顶部距离  (边框到边框)</li>
        <li>offsetWidth:用于获取元素的真实宽度（不含margin）</li>
        <li>offsetHeight:用于获取元素的真实高度（不含margin）</li>
        <li>offsetParent 相当于一个选择器 /获取的值为当前元素参考的定位父盒子</li>
        <li>parentNode 获取的父元素(紧贴着的父级标签) </li>
    </ul>
    <h4>scroll系列属性</h4>
    <ul>
        <li>scrollTop 元素的卷曲高度;计算方式，当元素内部的内容超出了元素的顶边框的底部时，开始进行计算</li>
        <li>onscroll 滚动事件（元素的内部滚动条滚动时触发事件）</li>
        <li>scrollLeft 元素卷曲的宽度（左侧的距离）;计算方式：当元素内部的内容超出了元素的左边框的右侧时，开始进行计算</li>
        <li>scrollHeight 元素内部内容的真实高度;计算方式为，从顶边框的底部，计算到内部内容的底部</li>
        <li>scrollHeight具有最小值;计算方式：顶边框和底边框之间的距离</li>
        <li>scrollWidth  元素内部内容的真实宽度</li>
        <li>获取页面的卷曲高度兼容性问题
            <ul>
                <li>window.pageYoffset(在ie9以下不支持)具有一个别名 window.scrollY(但是scrollY在ie中全都不支持，所以通常使用pageYoffset)</li>
                <li>document.body.scrollTop(只有谷歌好使)</li>
                <li>document.documentElement 指的是html标签(只有谷歌不好使)</li>
                <li> scrollTop: window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0</li>
                <li> scrollLeft: window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0</li>
             </ul>

        </li>
    </ul>
    <h4>client系类属性</h4>
    <ul>
        <li>标签.clientHeight 元素内部的真实高度;计算方式：边框到边框之间的距离;这个值与scrollHeight的最小值相同</li>
        <li>标签.clientWidth 元素内部的真实宽度;</li>
        <li>clientTop,clientLeft比較鸡肋： 相当于顶边框和左边框的宽度值;如果元素具有滚动条，并且左侧显示，会影响到clientLeft的取值（会增加17px的宽度）</li>
        <li>onresize 当页面尺寸改变时，触发事件</li>
        <li>获取可视区域宽度的方式
            <ul>
                <li>width: window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth || 0</li>
                <li>height: window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight || 0</li>
            </ul>
        </li>

    </ul>
    <p>可以把内容滚动到指定的区域：window.scrollTo(100, 200);参数1 表示横向的坐标;参数2 表示纵向的坐标</p>
</div>
<div>
    <h2>第十七天 事件</h2>
    <h4>简单事件与复杂事件，事件冒泡与事件捕获</h4>
    <ul>
        <li>事件三要素 ： 事件源 事件类型 事件处理程序;简单事件添加方式：事件源.事件类型 = function(){事件处理程序}</li>
        <li>问题：简单事件添加会出现覆盖的情况;解决：可以通过检测onclick属性中的值，如果是函数，说明添加过，如果什么也没有说明没加过</li>
        <li>标签.addEventListenter(eventtype(事件类型),fun（事件处理程序）,bl（事件的传递方式）)  添加事件监听;此方法在ie9以下不支持</li>
        <li>事件的移除方式：
            <ul>
                <li>1 简单事件的移除：btn.onclick = null;</li>
                 <li>语法 标签.removeEventListener(事件类型,事件处理程序)</li>
                <li>移除时，所有的参数必须与设置时完全相同;注意：第二个参数必须与设置时是同一个函数才行</li>
            </ul>
        </li>
        <li>ie11以下支持方法
            <ul>
                <li>注意点：只有ie11以下支持此方法（此方法注定要被淘汰，只不过是时间问题）</li>
                <li>在ie9以下注意执行的顺序相反（对实际的操作不影响）</li>
                <li>标签.attachEvent()移除方式  标签.detachEvent("onclick",对应的事件处理程序)</li>
            </ul>
        </li>
        <li>事件的默认传递方式：事件冒泡;执行顺序，当前元素触发事件后，会向父元素进行传递，如果父元素具有相同的事件，进行触发</li>
        <li>事件捕获需要使用特殊的设置方式（了解）
            <ul>
                <li>addEventListener的第三个参数;值为bool值，默认值为false，false表示事件冒泡;设置值为true表示以事件捕获的方式触发事件</li>
                <li>事件捕获的执行顺序与事件冒泡的执行顺序相反</li>
                <li>attachEvent方法不支持第三个参数。意味着在ie的低版本中不支持事件捕获</li>

            </ul>
        </li>
        <li>一个事件的执行都有三个必要过程：1 事件捕获（从外到内）;2 当前操作元素的事件触发;3 事件冒泡（从内到外）</li>
        <li>注意：我们设置的事件冒泡和事件捕获只能决定这个事件在哪一个阶段进行触发;eventPhase 属性可以知道某一个事件触发时处于事件执行的哪一个阶段中</li>
        <li>事件传播阻止：同时取消两种传播方式  事件对象.stopPropagation();ie低版本中不支持此方法;可以取消事件冒泡：window.event.cancelBubble = true;</li>
        <li>取消标签的默认事件的两种方法：return false 可以阻止默认事件效果；事件对象.preventDefault();</li>
        <li>onmousemove：当鼠标在页面中进行移动时，实时获取鼠标的位置</li>
        <li>onmousedown 当鼠标点下去的时候触发事件</li>
        <li>onmouseup 当鼠标按键抬起的时候触发事件</li>
     </ul>

    <h4>事件对象</h4>
    <ul>

        <li>在其他浏览器中获取：事件处理程序中接受形参event/e;ie浏览器：window.event;兼容 e || window.event;</li>
        <li>type属性：可以获取当前这个事件的类型名,没有on</li>
        <li>针对页面可视区域的横纵坐标:事件对象.clientX 横坐标;事件对象.clientY 纵坐标</li>
        <li>pageX pageY 鼠标针对页面的坐标,ie低版本中无法使用此属性,使用clientX/clientY + 页面卷曲的宽度/高度解决这个问题</li>

    </ul>
    <h4> 事件委托(事件代理)：解决动态创建元素事件的问题。</h4>
    <ul>
        <li>借助事件冒泡的特性，将事件添加给父元素后可以保证无论是否是动态创建的p都可以具有事件</li>
        <li>可以在事件处理程序中接受一个参数event----形参，这个参数不是我们自己传递的而是js的事件机制帮我们传递的</li>
        <li>event.target 代表了当前事件真正触发的那个元素ie低版本中使用 event.srcElement</li>
        <li>对target进行兼容: var tar = event.target || event.srcElement;</li>
    </ul>

</div>
<div>
    <h2>第十八天 正则和递归</h2>
    <h4>正则表达式的作用：操作字符串 (匹配，替换，提取)</h4>
    <ul>
        <li>正则表达式两种声明方式：构造函数声明方式 var reg = new RegExp("abc");字面量声明方式 var reg = /abc/;</li>

        <li>五大内部类
            <ul>
                <li>var reg = /abc/;简单类：可以匹配含有abc的字符串</li>
                <li>var reg = /[abc]/;字符类标识：使用[],中括号整体表示一位字符;可以匹配到含有字符a或者b或者c的字符串</li>
                <li>var reg = /[^abcd]/;反向类是对字符类功能的扩展,在字符类的标识[后面书写一个^，可以表示反向,可以匹配到除了abcd以外的其他字符串</li>
                <li>var reg = /[a-n]/;对于匹配的字符数较多的情况，可以使用范围类</li>
                <li>var reg = /[a-h1-7D-O]/;组合类：对范围类书写的组合。匹配 a到h之间的小写字母和1-7之间的数字和D-O之间的大写字母。种匹配情况的书写顺序随意。</li>
            </ul>
        </li>
        <li>边界：^书写在正则的最开始部分,表示开头；将$书写在正则的最后位置，表示匹配结尾；如果同时在一个正则中书写了^和$，表示严格匹配：必须与内部的内容完全相同</li>
        <li>量词
            <ul>
                <li>当需要匹配的内容重复出现的次数较多时，可以使用量词</li>
                <li>{n} 表示可以出现的次数</li>
                <li>{n,} 可以出现的次数>=n</li>
                <li>{n,m} 可以出现的次数为n-m之间</li>
                <li>
                    预定义的三个量词使用
                    <ul>
                        <li>* 出现的次数可以为0次到多次</li>
                        <li>+ 出现的次数可以为1次到多次</li>
                        <li> ? 出现的次数可以为0或1次</li>
                    </ul>
                </li>
            </ul>
        </li>
       <li>
         |或则，匹配的字符为多个并且不确定：  var reg = /ab|bc|cd|it/;
       </li>
        <li>正则方法
            <ul>
                <li>正则对象.test()方法:正则方法;参数为字符串，表示使用正则表达式对字符串进行匹配，匹配成功返回true，失败返回false</li>
                <li>
                    字符串.replace();replace的第一个参数可以使字符串，也可以是正则表达式
                    <ul>
                        <li>g - 全局匹配,str.replace(/c/g, "z");基本的正则表达式匹配到一个结果后就会停止，如果希望匹配到所有满足条件的字符部分，可以使用匹配模式g</li>
                        <li>i - 忽略大小写,str.replace(/c/gi, "z")</li>
                        <li></li>
                    </ul>
                </li>
                <li>正则基本的提取方式(两种)：
                    <ul>
                        <li> 字符串.match(reg);参数为正则表达式，返回一个匹配到的数组</li>
                        <li>正则.exec();,参数为一个字符串，使用exec方法时，如果用一个正则对象进行多次匹配，结果为进行累计，如果第一轮匹配结束，会返回null，如果再次匹配，会从头开始</li>
                        <li>exec的分组提取功能：var reg = /(\w+)@(\w+\.\w+)(?:\.\w+)*/g;使用括号可以进行分组提取，如果某一个括号中的内容不需要进行提取，可以在这个括号的开始部分添加?:</li>
                    </ul>
                   </li>

            </ul>
        </li>
        <li>\s表示一个空格；\w表示除汉字外其他字符，\d表示数组</li>
    </ul>
    <h4>递归：函数内部自己调用自己</h4>
    <ul>
        <li>计算1-10之间所有数的和</li>
        <li>斐波那契数列（兔子）  1 1 2 3 5 8 13(可使用对象保存方法返回值的方式进行优化)</li>
     </ul>
</div>
<div>
    <h2>第十九天jQery</h2>
    <h4>js和jq区别</h4>
    <ul>
        <li>
            js:1 入口函数的个数只能有一个;2 简单基本的dom操作，十分繁琐;3 兼容性问题;4 容错性差;5 制作简单的运动十分麻烦
        </li>
        <li>jq实现入口函数的两种方式：$(document).ready(function(){});$(funtion())</li>
        <li>jq的入口函数执行要比js的入口函数早:
            <ul>
                <li>js的入口函数：页面完全加载（页面结构，图片）后执行。</li>
                <li>jq的入口函数：页面结构加载完毕后执行。</li>
            </ul>
        </li>
        <li>$是jq所暴露出来的一个全局变量;jQuery 是jq暴露的第二个全局变量,一般不使用，通常使用$的用法。($ === jQuery)
            <ul>
                <li>1 $("#btn1")  选择器功能</li>
                <li>2 $(function(){       })   入口函数功能</li>
                <li>3 $(document) 赋予原生js的dom对象以新的功能（加强）</li>
            </ul>
        </li>

    </ul>
    <h4>js对象与jq对象</h4>
    <ul>
        <li>DOM对象：js中原生提供的方法获取的dom元素;jq对象：通过jq的获取方式获取到的对象，称为jq对象</li>
        <li>jq的方式获取的jq对象实际上是将原生的DOM对象进行了包装，再赋予新的功能,也称jq对象实际上是dom对象的包装集</li>
        <li>导致的特性，dom对象与jq对象的功能相互独立：dom对象只能使用dom的属性和方法;jq对象也只能使用jq提供的属性和方法</li>
        <li>将dom对象转换为jq对象（使用$()进行包裹即可）</li>
        <li>将jq对象(一个数组)转换为dom对象（取出jq对象内部的dom对象）dom=jq[0]</li>
    </ul>
    <h4>jq选择器</h4>
    <ul>
        <li>1 标签选择器:  $("标签名")</li>
        <li>2 id选择器:  $("#id名");</li>
        <li>3 类选择器：  $(".类名");</li>
        <li>4 交集选择器:$("p.box")</li>
        <li>5 并集选择器: 使用逗号分割，一次选取多种标签:$("div.box,p")</li>
        <li>6 层级选择器
            <ul>
                <li>1 子代选择器   >标识 $("ul>li")</li>
                <li>2 后代选择器  空格标识  $(".list li")</li>
            </ul>
        </li>
        <li>
            基本选择器
            <ul>
                <li>:even 获取到索引值为偶数的元素</li>
                <li>:odd 获取到索引值为奇数数的元素</li>
                <li>:eq(索引值) 按照索引值=获取某一个元素</li>
            </ul>
        </li>
        <li>
            过滤选择器：都是方法的书写形式。好处：灵活性强，完善链式编程的书写方式。
            <ul>
                <li>children() 获取子元素中的某些指定元素</li>
                <li>find() 找到后代中满足条件的元素</li>
                <li>parent()  获取父元素，注意与parentNode的区别，可能获取到多个元素</li>
                <li>siblings() 同级元素</li>
                <li>eq(索引值)</li>
                <li>next() 获取后一个同级元素</li>
                <li> prev() 获取前一个同级元素</li>
            </ul>
        </li>
    </ul>
    <h4>jq的两大特性</h4>
    <ul>
        <li> 隐式迭代(遍历)：当我们使用jq获取了多个元素时不需要遍历，直接对进行对象进行操作;操作会应用与内部获取到的所有dom对象上</li>
        <li> 链式编程：当执行过任意的设置型操作后，均可继续操作原有对象</li>
        <li> 隐式迭代操作同样只对设置型操作有效果，对获取操作无效（原因是无法减少操作的步骤）</li>
    </ul>
    <h4>jq方法</h4>
    <ul>
        <li>index() 可以获取在同级元素中的索引值</li>
        <li>css方法用于进行样式的操作
            <ul>
                <li>设置操作 : 样式名可以使用驼峰命名法和css的形式，但是推荐使用驼峰命名的方式;如果样式的值需要单位，单位可加可不加</li>
                <li>css方法可以一次进行多个样式的修改：传入一个对象</li>
                <li> 获取样式的值:$("#box").css("width");获取时，只能传入一个参数，如果是带单位的样式，结果具有单位，字符串格式</li>

            </ul>
        </li>
        <li>显示使用show();隐藏使用 hide();</li>
        <li>
            移入事件: mouseover()会产生冒泡    mouseenter()不会产生冒泡;移出事件: mouseout() 会产生冒泡    mouseleave()不会产生冒泡
        </li>
    </ul>
</div>
<div>
    <h2>第二十天jq类名操作丶动画与节点操作</h2>
    <h4>类名操作</h4>
    <ul>
        <li>添加类名: addClass()</li>
        <li>移除类名: removeClass();移除时不需要考虑设置时的书写顺序</li>
        <li>切换类名: toggleClass();检测，传入的类名是否存在，如果有，移除，如果没有，添加</li>
        <li>检测是否拥有类名:hasClass(); 返回bool值
            <ul>
                <li>检测单个标签的单个类名(推荐的用法)</li>
                <li>检测单个标签的多个类名(通常不会进行多个类名的同时检测，除非可以确定类名书写的顺序否则，即便书写正确，但是位置不对，依然返回false)</li>
                <li> 多个标签的类名检测:检测时，多个标签中只要某个标签具有对应的样式，返回true，不准确</li>
            </ul>
        </li>
    </ul>
    <h4>三组jq自动动画与自定义动画</h4>
    <ul>
        <li>
            show()和hide();
            <ul>
                <li>不传入参数表示显示，没有运动</li>
                <li>参数1，表示运动时间，毫秒为单位;参数1 允许传入字符串:  "fast"200  "normal"400  "slow"600;</li>
                <li>如果传入的字符串为其他内容，默认为"normal"400毫秒。</li>
                <li>运动时修改的样式为宽 高 透明度</li>
                <li>参数2 回调函数，内部的代码会在运动完毕后执行</li>

            </ul>

        </li>
        <li>
            slideDown()滑入（拉下来）和slideUp() 滑出（上拉）;slideToggle()切换
            <ul>
                <li>参数1 表示运动时间，如果不传，默认为400毫秒;传入的形式可以为 毫秒的数值 或者 3种字符串</li>
                <li>参数2 回调函数，运动完毕后执行</li>
             </ul>
        </li>
        <li>
            fadeIn() 渐入；fadeOut()渐出；fadeToggle()  切换；fadeTo();渐到什么程度，参数为透明度，通常我们不推荐使用这个方法，原因是，会对其他的运动操作造成影响
        </li>
        <li>animate();
            <ul>
                <li>参数1 必选 要进行运动的样式以及运动到的值 , 对象形式</li>
                <li>参数2 可选 运动时间:可以为毫秒或者字符串形式</li>
                <li>参数3 可选 运动模式 默认为"swing" 缓动 , 可设置为"linear" 匀速</li>
                <li>参数4 可选 回调函数:运动结束执行</li>

            </ul>
        </li>
        <li>stop();
            <ul>
                <li>参数1 是否清除动画队列  true表示清除，false表示不清除</li>
                <li>参数2 是否跳到当前动画的最终效果  true表示跳到最终效果  false不跳</li>
                <li>两个参数的默认值均为false;false,false 最终需要的效果,最常用</li>
            </ul>
        </li>
    </ul>
    <h4>节点操作</h4>
    <ul>
        <li> 创建元素的方式:
            <ul>
                <li>1 $("标签");</li>
                <li>2 指定某个标签内内部结构创建:html()相当于innerHTML,$("#box").html("标签");</li>
            </ul>
        </li>
        <li>
            4中基本的节点移动方式
            <ul>
                <li>append():与原生的appendChild的区别:如果添加的位置具有多个，可以同时添加到多个位置(dom方法不可以)</li>
                <li>prepend() 添加位置为元素的子节点最前面</li>
                <li>after() 向同级元素的前添加</li>
                <li>before() 向同级元素的后添加</li>
                <li>对应的4种：区别在于是当前调用方法的元素加到方法里面元素中；目的是为了完善链式编程的用法。
                    <ul>
                        <li>appendTo();</li>
                        <li>prependTo();</li>
                        <li>insertAfter();</li>
                        <li>insertBefore();  跟dom中的insertBefore完全无关</li>
                    </ul>
                </li>

            </ul>
        </li>
        <li>移除节点
            <ul>
                <li>想要删除box内部的元素,$("#box").html("");这写标签的结构被删除掉了，实际上如果内部元素具有事件，这时事件无法移除。</li>
                <li>empty() 可以移除标签内部的所有内容  ( 感觉被掏空 )</li>
                <li>想要删除当前元素内部内容以及自身  remove()  ( 自杀 ) detach() 不能删除事件</li>
            </ul>
        </li>
    </ul>
    <p>
        动画队列:当一个一个运动没有完毕时，立刻添加一个新的动画， 这时旧的动画会继续执行，新的动画需要进行等待.会导致动画的积累，这种效果称为动画队列
    </p>
</div>
<div>
    <h2>第二十一天 事件</h2>
    <h4>属性和样式操作</h4>
    <ul>
        <li>attr方法用于操作标签的行内属性（自定义属性 获取某些自带属性时取值不准确：类似disabled等属性的值为非bool值)</li>
        <li>prop方法用于操作标签的行内属性（自带属性）无法操作自定义属性</li>
        <li>样式获取操作：
            <ul>
                <li>通过css的方式获取样式值：宽</li>
                <li>$("#box").width() 获取宽度的数值，数值类型 只含有width的值</li>
                <li>innerWidth() 数值类型  含 width + padding</li>
                <li>outterWidth() 含  width + padding + border</li>
                <li>outterWidth(true)  含  width + padding + border + margin</li>
            </ul>
            样式设置操作：
            <ul>
                <li>width()只有width修改</li>
                <li>$("#box").innerWidth(200);修改的是width + padding的总和    修改的具体值为width的值</li>
                <li>$("#box").outerWidth(200);修改的是width + padding + border 的总和    修改的具体值为width的值</li>
                <li>修改的是width + padding + border +margin 的总和  修改的具体值为width的值;传参方式：先传数，再传true</li>

            </ul>
        </li>

    </ul>
    <h4>scroll,offset,opsition</h4>
    <ul>
        <li>scrollTop() 卷曲高度;scrollLeft()  卷曲宽度</li>
        <li>使用offset()可以得到一个对象值
            <ul>
                <li>是相对于页面的,与原生js的offset系列属性有所不同</li>
                <li>设置offset值时需要使用对象的形式，并且设置的值必须是数值类型</li>
                <li>如果元素没有设置定位，默认方法会给元素添加一个相对定位（不要让方法去加，自己设置即可）</li>
            </ul>
        </li>
        <li>position() 获取的样式值是参考定位的父盒子的值
            <ul>
                <li>获取的结果与offsetLeft等 略有不同，取得的值不会包含margin的值</li>
                <li>无法设置，只读(下面的代码不会生效)</li>
                <li></li>
            </ul>
        </li>

    </ul>
    <h2>事件的</h2>
    <ul>
        <li>使用bind方法管理;$("#btn").bind("click mouseenter mouseleave",function () {}),参数可以是对象</li>
        <li>delegate()  可以添加事件委托 事件代理 $("#box").delegate("p,span","click",function () {})</li>
        <li>最终jq使用on()统一了所有的事件添加方式
            <ul>
                <li>1 简单事件添加;$("#btn").on("click",function () {}).参数可以是对象</li>
                <li>2 添加多个事件;$("#btn").on("click mouseenter mouseleave",function () {//多个事件共用一个处理程序})</li>
                <li>3 事件委托的方式 注意参数传递顺序：  参数1 事件类型  参数2 指定哪些内部元素可以触发  参数3 事件处理程序</li>
                <li>$("#box").on("click", "p,span", function () {//将事件委托的box，而不是直接添加给内部的子元素})</li>
            </ul>
        </li>
        <li>事件委托先执行，然后在执行box自身设置的事件</li>
        <li>
            事件的取消
            <ul>
                <li>1 移除指定事件(移除各种各样的指定事件) $("#box").off("click");</li>
                <li>1.1 如果想要移除指定事件类型中的某一个事件，可以传入参数2，事件处理程序 $("#box").off("click", fun);</li>
                <li>1.2 移除事件委托 $("#box").off("click","**");</li>
                <li>2 同时移除多种事件 $("#box").off("click mouseenter");</li>
                <li>3 移除box的所有事件 $("#box").off();</li>
            </ul>
        </li>
        <li>
            事件的触发方式
            <ul>
                <li>手动触发点击事件 $("#btn").click();</li>
                <li>使用trigger方法触发某个事件，与基本的触发方式大体相同 $("#btn").trigger("click");（标准的触发方式，会执行事件代码以及触发事件执行的页面显示效果）</li>
                <li>$("#btn").triggerHandler("click"); 只会触发代码的执行， 不会出现页面显示上的变化</li>
             </ul>
        </li>
        <li>取消事件传播(jq中的使用方式：)
            <ul>
                <li>e.preventDefault();//可以取消标签的默认事件效果</li>
                <li>e.stopPropagation();//阻止事件传播</li>
                <li>return false;可以取消标签的默认事件效果，同时可以阻止事件的传播</li>
                <li>return false在js中只能取消标签的默认事件效果.</li>

            </ul>
        </li>
    </ul>
    <h4>其他</h4>
    <ul>
        <li>e.keyCode获取按键对应的值</li>
        <li>多库共存
            <ul>
                <li>jq具有不同的版本，新版本的功能的效率基本上都高于低版本。;有的时候由于浏览器版本的限制，不得不使用旧版本。</li>
                <li>如果一个页面需要在多种浏览器上使用，可能根据使用的浏览器不同，使用的版本也不同。</li>
                <li>$.fn.jquery 该属性可以查看jq的版本号</li>
                <li>var $2 = $.noConflict() jq内部提供了一个方法，可以放弃jq对$的控制权，返回值为jq的使用权,我们可以将使用权赋予全新的一个变量。</li>
            </ul>
        </li>
        <li>图片懒加载插件</li>
        
    </ul>
    <h5>js文件第一行加 use strict 开启严格模式</h5>
</div>
</body>
</html><file_sep>## cookie
- cookie的尺寸4kb影响的是一个域下的所有的cookie，并非是对单条cookie的限制。
- cookie在每个浏览器的条数是不同的，最少的如ie6限制是每个域名最多20个。如果一个域名下要存储大量的cookie，可以采用子cookie的形式，但cookie的尺寸依旧受限制。
- cookie存储的任何数据都可以被其他人获取到，因此不要存储太过重要的信息。
## web存储机制
### Storage类型
- Storage类型只能存储字符串。非字符串的数据在存储之前会被转换成字符串。
- sessionStorage对象（5M）
 + 存储特定某个会话的数据，只能在当前存储的页面访问到，可以跨越页面刷新而存在。
 + 因为sessionStorage对象绑定某个服务器会话，因此文件在本地运行是没有办法访问到的。
 + sessionStorage对象是Storage的一个实例。
- globalStorage对象（在h5中被localStorage对象替代）
  + 使用它要指定哪些域名可以访问。
  + globalStorage['域名']才是Storage的实例。
  + 在某些不支持localStorage对象的浏览器中，可以使用globalStorage[location.host]来模拟localStorage对象。
- localStorage对象（20M）
  + 要访问同一个localStorage对象，页面必须来自同一个域名（子域名无效），使用同一种协议，在同一个端口上。
  + localStorage对象是Storage实例。
  + 可以持久保存数据。
- storage事件：对Storage对象进行任何修改，都会在文档上触发storage事件。
### indexedDB 浏览器保存结构化数据的一种数据库
<file_sep># 第一部分 类型和语法
## 类型
### 内置类型
   - Javascript有七种内置类型：
      + 空值 (null)
      + 未定义 (undefined)
      + 布尔值 (boolean)
      + 数字 (number)
      + 字符串 (string)
      + 对象 (object)
      + 符号 (symbol,ES6新增)
   - typeof 复合条件检查null类型：var a=null; (!a&& typeof a==="Object")   
   - typeof Undeclared(未声明变量 not defined)。未声明的变量在全局作用域中会报错。但使用操作符typeof对其进行操作不会报错，会返回一个undefined，虽然实际上未声明变量的类型并不是undefined，是因为typeof的安全机制会对未声明的变量进行处理。所以typeof可以和window.属性起到一样的作用，即判断属性是否存在。
   - 变量没有类型，变量的值才有类型
   - 依赖注入，就是显示的把参数传入到函数中。
  ## 值
  ### 数组
  - delete可以删除数组的一个项，但数组长度不发生变化，删除的项值为uudefined。
  - 数组也是对象，可以包含对象的键值对，但加入键值对不影响数组的长度。但是如果这个键能被强制转换为number类型的话，它会被当成数字索引来处理。
````js
    var arr=[];
    arr.name="zzz";
    console.log(arr.name);//zzz
    console.log(arr.length);//0
````
 - 类(伪)数组转数组：Array.prototype.slicec.call({0: "tt", length: 1});ES6方法：Array.from({0: "tt", length: 1});
 ### 字符串
 - 字符串是类数组，可以借用数组的许多方法。本身和数组有一些一样的方法，如length属性，indexOf方法和concat方法。
 - 字符串本身并没有反转的方法，也不能“借用”数组的可变更成员函数，因为字符串是不可变的。但有一个变通的方法，可以先把字符串转换为数组进行反转，然后在拼接成字符串。
   + 但是这种方法只能处理简单的字符串，包含复杂字符（Unicode,如星号，多字节字符等就是不合适）这时需要功能更完善的Unicode工具库。参考：https://github.com/mathiasbynents/esrever.
   + 如果要经常使用字符数组来处理字符串，不如直接使用数组，在转换为字符串。
 ````js
     var str="woainiqinaideguniang";
     var strArr=str.split("");
     strArr.reverse(); //反转数组操作的是当前数组本身
     str=strArr.join("");
 ````
### 数字
 - 特别大或则特别小的数字用指数格式显示，与toExponential()函数输出结果相同。
 ````js
 var a=5e10; //50000000000
a.toExponential();//"5e+10"
 ````
 - 特殊数值
   + null指空值，曾赋过值，但目前没有值。undefined指从未赋值
   + Javascript的变量指向指，而不会指向其它变量
- 一般不推荐使用原生构造函数创建对象，如 Object(),Function(),RegExp(),但也有例外，当要创建动态正则表达式时，可以用new RegExp() 的方式。
- 构造函数Error带不带关键字new都一样。常和throw一起使用。
````js
function foo(x){if(!x){throw new Error("x wasn`t provided")}}
````
- 字符串API（以下方法不会改变原字符串，而是返回一个新字符串）
   + String.indexOf()在字符串中查找字串出现的位置
   + String.charAt()获取字符串在指定位置上的字符
   + 获取字符串的指定部分
       + substr()第一个参数为开始位置，第二个参数为返回字符个数，如果第一个参数为负数，则从后往前数。
       + subsgring()第一个参数为开始位置，第二个参数为结束位置，如果有一个参数为负数，可以把负数当成0处理。
       + slice()第一个参数为开始位置，第二个参数为结束位置，第二个参数为负数意味着从后往前查找。
    + toUpperCase和toLowerCase将字符串转大写或小写。
    + trim去掉字符串空格，返回新字符串。   
  - 构造函数原型默认值，可以当成变量来用
     + typeof Function.prototype  空函数
     + RegExp.prototype.toString() 空正则表达式
     + Array.isArray(Array.prototype) 空数组

## 强制类型转换
- 类型转换发生在静态类型语言编译阶段，强制类型转换则发生在动态类型语言运行时。
- JSON字符串化
   + 不安全的JSON值有undefined，function，symbol和包含循环引用的对象。用JSON.stringify()进行转换，前三者会被忽略，循环引用对象会报错。在数组中出现前三者，会给一个null值。
   ````js
      JSON.stringify([function(){}])//[null]
   ````
   + 如果要对含有非法的JSON值的对象字符串化，或则有些值无法被序列化，就需要定义一个toJSON()方法来返回一个安全的JSON值。原因是因为如果对象中定义了toJSON()方法，JSON字符串化时会先调用该方法，用它的返回值进行序列化。
     + toJSON返回的应该是一个适当的值，可以是任何类型，然后再由JSON.stringity()对其进行字符串化，而不是返回JSON字符串化后的值。
     + JSON.stringity(),还可以传入第二个参数。可以是数组或则函数。如果是数组，那么它必须是字符串数组，其中包含要被序列化属性的名称。如果是函数，那么它会对对象本身调用一次，然后对对象的每个属性依次调用，传入两个参数，键和值。如果replacer是函数，它的第一次调用时为undefined（就是对象本身调用的那次）
     + JSON.stringity()第三个可选参数时space，用来指定缩进的格式。
     + 如果传递给 JSON.stringity()的对象中定义了toJSONto()方法，那么该方法会在字符串转化前调用，以便将对象转化为安全的JSON值。
   ````js
    var o={};
    var a={
        b:42,
        c:o,
        d:function(){}
    }
    o.e=a;
    //JSON.stringify(a); //报错
    a.toJSON=function(){
        return {b:this.b}; //仅对b进行序列化
    }
    JSON.stringify(a);//"{"b":42}"

    JSON.stringify(a,["b"]); //"{"b":42}"

    JSON.stringify(a,function(k,v){if(k!=="c"&&k!=="d")return v}) //"{"b":42}"
   ```` 

+ Number
  - 对象（包括数组）会首先被转化为相应的基本类型值，如果返回的是非数字的基本类型值，则再将其强制转化为数字。
  - 未来将值转化为相应的基本类型值，抽象操作ToPrimitive会首先检查该值是否拥有valueOf()方法。如果有并且返回基本类型值，则使用该值进行转化。如果没有就使用toString()的返回值（如果存在）来进行强制类型转化。
  - 如果valueOf()和toString()均不返回基本类型值，则会产生TypeError()错误。
  ````js
    var arr=[2,3,4];
    arr.toString=function(){return this.join("")}//修改数组本身的toString方法
    Number(arr);
  ````
 + Boolean
    - 假值：false,undefined,null,"",NaN,+0,-0

 + 显示类型转化
   - +new Date()获取当前时间戳
     + Date.now()
     + new Date().getTime()
   - 奇特的~运算符
     + ~x大致等同于-(x+1);在-(x+1)中唯一能得到假值的是当x等于-1的时候。
     ````js
         var str="ttaamm"
         if(~str.indexOf("b")){} //取反 !~str.indexOf("b")
         //比起使用>=0和==-1写法上要好很多，后者称为“抽象渗漏”意思是代码暴露了底层的实现细节。
     ````
    - 显示解析数字字符串
      + 解析：parseInt，针对的是字符串，所以传递数字和其它类型无用
      + 转换：Number 
    - 字符串和数字之间的隐式强制类型转化
       + a+""(隐式)和前面的String(a)（显示）之间有一个细微的差别需要注意，根据ToPrimitive抽象操作规则，a+“”会对a调用valueOf()方法，然后通过ToString抽象操作将返回值转化为字符串。而String(a)则直接调用ToString()方法。它们最后返回的都是字符串，但如果a是对象而非数字结果可能不一样。
       ````js
         var a={
             valueOf:function(){
                 return 12;
             }
             toString:function(){return 24;}
         }
         a+""; //12
         String(a); //24
       ````
     - ||和&&
       + 对于||来说，如果判断条件为true，则返回第一个。 如果为false则返回第二个。
       + &&则相反，如果判断结果为true，返回第二个，否则返回第一个。

     - 宽松相等和严格相等正确的解释是："=="允许在相等比较中进行强制类型转化，而===则不允许。 常见的误区是：===检查值和类型是否相等。      
        + ==和===都会检查值的类别，区别在于操作数类型不同时它们的处理方式不同。
        + 在==比较中，一个为数字，一个为字符串，则字符串通过Number转化为数字进行比较
        + 在==比较中，如果有一侧出现Booloean，则Booloean值进行Number转化后比较。
        + 在==中，null和undefined相等，和自身也相等。和其它假值不相等（如false等）
        + Number({})为NaN,Number([])为0。
        + 如果==两侧类型一样，不会发生类型转化。
      - 安全运用隐式类型转化
        + 如果两边的值中有true或则false，千万不要用==。
        + 如果两边值中有[],""或则0，尽量不要使用 ==。  
      -   a<=b：Javascript中<=是“不大于”的意思理解为!(a>b)。
## 语法      

# 性能和异步
## 回掉 
   ### 省点回掉
     - 保证调用一定是异步
````js
   function anyncifn(fn){
            var orig_fn=fn,
            intv=setTimeout(function(){
                intv=null;
                //如果没有给fn重新赋值，说明fn已经执行过了
                fn&&fn();
            });
            fn=null;//把fn置空
            return function(){
                //如果调用了定时器则为fase，也就是在异步的情况下会出现false
                //如ajax请求，当ajax进行回掉时，定时器已经执行完成，直接调用函数就可以了
                //如果是一般调用，则让fn给定时器执行，保持回掉方法的异步执行。
                if(intv){
                  //给fn重新赋值，让它在定时器中调用
                  //bind.apply 猜测这种用法是为了传递的参数为一个数组
                    fn=orig_fn.bind.apply(
                        orig_fn,
                        [this].concat([].slice.call(arguments))
                    )
                }else{
                    orig_fn.apply(this,arguments);
                }
            }
        }
        var a=0;
        function fn(){
            console.log(a);
        }
        anyncifn(fn)();
        a++;
````
## Promise
  ### Promise信任问题
    - 回掉未调用：Promise提供了解决方案，使用了一种称为竞态的高级抽象机制。
````js
     function timeoutPromise(delay){
       return new Promise(function(resolve,reject){
            setTimeout(function(){ reject("Timeout!") },delay)
       })
     };
     //应该是rece的竞态机制导致的，只要有一个Promise决议了，就执行回掉函数。
     Promise.race([foo(),timeoutPromise(3000)]).then(
       function(){
        // foo()及时完成
       }，
       function(err){
         //或则foo()被拒绝，或则只是没能按时完成
       }
     )

```` 
    - 调用次数过多或过少
      + Promise只会接受第一次决议，后续的决议都会忽略。对应的被注册的所有响应函数也只会被调用一次。   

    - Promise.reject()和Promise.resolve()区别，前者不会像后者一样进行展开，如果向reject()传递一个Promise/thenable值，这个值原封不动的设置为拒绝理由。后续的拒绝处理函数接受的是实际传递给reject()的那个Prommise/thenable，而不是其底层的立即值。提供给then的回掉，推荐使用：fulfilled()和rejected();

## 生成器
  - 生成器的本质就是生成一个迭代器，迭代器会执行完函数或遇到yield（屈服）停下。yieid会导致生成器在执行过程中发送出一个值，类似于中间的return。
 ````js
 var x=1;
  function *foo(){
    x++;
    yield; //暂停
    console.log("x:",x);
  }
  function bar(){
    x++;
  }

  var it=foo();//构造一个迭代器it来控制这个生成器
  it.next();//启动foo()
  x;    //2
  bar(); 
  x;  //3
  it.next();  //x:3
 ```` 
 ### 输入和输出
   - 生成器是一个特殊的函数，只是具有新的执行模式，可以输入参数和返回值。间接的用迭代器控制生成器
 ````js
   function *foo(x,y){
     return x*y;
   }
   var it=foo(6,7);
   var res=it.next();
   res.value;//42
 ````
   - yield和next()这一对组合起来，在生成器的执行过程中构成了一个双向消息传递系统。
      + 本质上是第一个next先发起一个话题，如果发现yield则停下，看看yield是否有给它传递消息。有消息则把消息返回。然后第二个next()可以给第一个yield传递消息，顺带接受下一个yield的消息，没有的话接受reture的消息。第一个next只是起到启动生成器的作用，如果带入参数会被忽略掉，只能被动接受第一个yield给的消息。
 ````js
     function *foo(x){
        var mm={name:"zsn"};
        return y=x*(yield mm) ; //把mm返回给迭代器

    }
    var it=foo(5);
    var res= it.next();
    console.log(res.value);  //{name:"zsn"}
    res=it.next(8);    //给yield传递参数
    console.log(res.value); //40
 ````   
  - 多个迭代器
    - 每次构建一个迭代器，实际上就是隐式构建了一个生成器的实例，通过迭代器来控制生成器的实例。
    
  ````js
    function *foo(){
        var x=yield 2;
        z++;
        var y=yield(x*z);
        console.log(x,y,z);
    }
    var z=1;
    var it1=foo();
    var it2=foo();
    var val1=it1.next().value;  //返回第一个yield后面的2
    var val2=it2.next().value;   //返回第一个yield后面的2

    val1=it1.next(val2*10).value;
    //val2*10=》2*10 替换第一个yield，让x=20，z加1，z=2，返回第二个yield后面的值 x*z=》40.
    val2=it2.next(val1*5).value;
   // val1*5=》40*5 替换第一个yield，让 x=200，z加1，z=3，返回第二个yield后面的值 x*z=》200*3=>600
    it1.next(val2/2);
  // val2/2=>600/2=300 替换第二个yelid的值，让 y=300. 则 x=20 y=300 z=3；
    it2.next(val1/4);
    // val1/4 =》40/4=10   替换第二个yield的值 让 y=10 则 x=200 y=10 z=3;
  ````
    - 注意yield前面的语句都是执行了的，哪怕在同一行中，变量都会替换为对应的值。
  ````js
    //yield 迭代器，第一个迭代器当前yield值带出，第二个迭代器在把当前yield替换为第一个迭代器带出的值。
      function step(gen){
        var it=gen();
        var last;
        return function (){
            last= it.next(last).value;
        }
    }



    function *foo(){
        a++;
        yield;
        b=b*a;
        a=(yield b)+3;
    }
    function  *bar(){
        b--;
        yield;
        a=(yield 8)+b;
        b=a*(yield 2);
    }

    var a=1;
    var b=2;
    var s1=step(foo);
    var s2=step(bar);

    s2();  // b-- b=1
    s2();  // 8
    s1();  // a++ a=2
    s2(); // a=9

    s1();  //b=9

    s1();  //a=12
    s2(); // b= 18  
   console.log(a,b);  
  ```` 
### 生成器+Promise
  - 生成器yield出Promise，然后其控制生成器迭代器来执行它，直到结束。
  ````js
       function run(gen){
        //获取除了第一个函数以外的所有参数
        var args=[].slice.call(arguments,1),it;
        //把参数传递给生成器，返回一个生成器对象
        it=gen.append(this,args);
        return Promise.resolve().then(function handleNext(value){
            //调用迭代器
            var next=it.next(value);
            return (function handleResult(next){
                //判断生成器是否执行完成
                if(next.done){
                    return next.value;
                }//否则继续执行
                else{
                    return Promise.resolve(next.value).then(
                            //成功就恢复异步循环，把决议的值返回给生成器，继续迭代
                            handleNext,
                            //如果value是被拒绝的promise，就把错误传回生成器进行错误处理
                            function handkeErr(err){
                                return Promise.resolve(it.throw(err)).then(handleResult);
                            }
                    )
                }
            })(next)
        })
    }

    //简单的调用
    function *main(){

    }
    run(main);
    //这种运行run()的方式，它会主动运行传递给它的生成器，直到结束。
  ````
  ### 生成器委托
   - 在一个生成器中调用另一个生成器，可以把被调用的生成器的迭代委托调用者进行迭代。语法yield *生成器名称
  ````js
     function *foo(){
         console.log("*foo start");
         yield 3;
         yield 4;
         console.log("*foo finished");
     }
    function *bar(){
        yield 1;
        yield 2;
        yield *foo();  //yield委托
        yield 5;
    }
    var it=bar();
     console.log(it.next().value); //1
     console.log(it.next().value); //2
     console.log(it.next().value); //*foo start 3
     console.log(it.next().value); //4
     console.log(it.next().value); //foo finished 5
     console.log(it.next().value); //
  ````

  ## 性能测试与调优
    - 尾调用优化（TCO）ES6包含了一个性能领域的特殊要请，涉及到函数调用的特定优化方式。
       + 调用一个新的函数需要额外的一块预留内存来管理调用栈，称为栈帧。所以在一个函数的尾部调用一个新的函数，新函数会直接用当前函数的栈帧，它就不会创建一个新的了。
       + 在一般代码中，这个优化并不算什么，但在递归中，这个解决了大问题，特别是递归的数量大时。
       + 只有在函数结尾单纯的调用新函数，而没有其它操作才被javascript引擎识别为TCO友好的尾调用。
       + 如果没有TCO，引擎需要实现一个随意（还彼此不同）的限制来界定递归的深度，达到了就停止，以防止内存耗尽。
       + 缺乏TCO会导致一些javascript算法因为害怕内存耗尽调用栈限制而降低了通过递归实现的效率。
  ````js
      function factorial(n){
        function fact(n,res){
            if(n<2)return res;
            return fact(n-1,n*res);
        };
        return (n,1);
    }
  ````<file_sep># http服务&ajax编程

## 1、服务器

> 前言：通俗的讲，能够提供某种服务的机器（计算机）称为服务器

#### 1.1、服务器类型

按照不同的划分标准，服务可划分为以下类型：

1. 按**服务类型**可分为：文件服务器、数据库服务器、邮件服务器、Web服务器等；
2. 按**操作系统**可分为：Linux服务器、Windows服务器等；
3. 按**应用软件**可分为 Apache服务器、Nginx 服务器、IIS服务器、Tomcat服务器、
   weblogic服务器、WebSphere服务器、boss服务器、 Node服务器等；

#### 1.2、服务器软件

使计算机具备提供某种服务能力的应用软件，称为服务器软件，
通过安装相应的服务软件，然后进行配置后就可以使计算具备了提供某种服务的能力。

常见的服务器软件有：

1. 文件服务器：Server-U、FileZilla、VsFTP等（FTP是File Transfer Protocol文件传输协议）；
2. 数据库服务器：oracle、mysql、SQL server、DB2、ACCESS等；
3. 邮件服务器：Postfix、Sendmail等；
4. **HTTP服务器**：Apache、Nginx、IIS、Tomcat、NodeJS等；


#### 1.3、HTTP服务器

即网站服务器，主要提供文档(文本、图片、视频、音频)浏览服务，一般安装Apache、Nginx服务器软件。
  
HTTP服务器可以结合某一编程语言处理业务逻辑，由此进行的开发，通常称之为**服务端开发**。 
 
常见的运行在服务端的编程语言包括 php、java、.net、Python、Ruby、Perl等。  



## 2、客户端

具有向服务器**索取服务**能力的终端，如比如 手机、电脑等，通过安装不同的客户端软件，
可以获取不同的服务，比如通过QQ获得即时通讯服务、通过迅雷获得下载服务等。

常见的客户端软件：浏览器、QQ、迅雷、Foxmail等。

以浏览器为宿主环境，结合 HTML、CSS、Javascript等技术，而进行的一系列开发，通常称之为**前端开发**。


## 3、网络基础

#### 3.1 IP地址

所谓IP地址就是给每个连接在互联网上的主机分配的一个32位地址。(就像每部手机能正常通话需要一个号码一样)

查看本机IP地址 ping、ipconfig、ifconfig（linux）


#### 3.2、域名

由于IP地址基于数字，不方便记忆，于是便用域名来代替IP地址，域名是一个IP地址的“面具”  

查看域名对应的IP地址 ping


#### 3.3、DNS服务

DNS（Domain Name System）因特网上作为域名和IP地址相互映射的一个分布式数据库，
能够使用户更方便的访问互联网，而不用去记住能够被机器直接读取的IP数串。

简单的说就是记录IP地址和域名之间对应关系的服务。

查找优先级 本机hosts文件、DNS服务器

ipconfig /flushdns 刷新DNS


#### 3.4、端口

端口号是计算机与外界通讯交流的出口，每个端口对应不同的服务。

*现实生活中，银行不同的窗口办理不同的业务。*

查看端口占用情况 netstat -an

常见端口号 80、8080、3306、21、22

## 4、软件架构

#### 4.1、C/S结构

即**C**lient、**S**erver

C/S工作流程图
 
![alt_text](m_01.png)
 
在C/S结构的情况下，不同的服务需要安装不同的客户端软件，

比如QQ、迅雷、Foxmail这种情况下安装的软件会越来越多，同时也有许多弊端，

比如A出差，需要在B电脑上查收邮件，但是B电脑并未安装Foxmail等类似的客户端软件，

这样不得不先去下载Foxmail，非常不方便。


#### 4.2、B/S结构

B/S（即**B**roswer、**S**erver）解决了C/S所带来的不便，

将所有的服务都可以通过浏览器来完成（因为基本所有浏览器都安装了浏览器），

但B/S也有一些不利，比如操作稳定性、流畅度等方面相对较弱。

## 5、搭建HTTP服务

>Windows + Apache + Mysql + PHP，首字母组合。

#### 5.1 安装WampServer

安装wampserver，和普通软件安装无差别，除指定安装路径外，其它默认安装。

#### 5.2 管理HTTP服务

任务图标**绿色为正常启动**状态  
通过图形控制台可以启动、重启、停止所有服务  
![](m_07.png)
 
或者单独启动、重启、停止特定服务  
![](m_08.png)
 
**注意事项：**  
1、检查网络是不是通的 ping 对方IP  
2、检查防火墙是否开启，如果开启将不能正常被访问  
3、检查访问权限 **Allow from all**   
4、理解默认索引  
5、确保端口没有被**其它程序**占用  
6、“#”表示注释  
7、修改配置要格外小心，禁止**无意修改**其它内容  

#### 5.3 配置根目录

网站根目录是Web服务器上存放网站程序的空间，可通过修改配置文件自定义，如E:/www

**具体步骤如下：**   
1、打开配置文件，控制台选择  
![](m_02.png)  
或者 wampserver安装目录下bin\apache\Apache2.2.21\conf\httpd.conf
  
2、设定根目录，查找并修改  
![](m_03.png)     
例如：  
![](m_04.png)   
这样就指定了 "E:/www/"为存放网站的根目录。
  
3、配置根目录，查找  
![](m_05.png)     
修改成   
![](m_06.png) 

4、修改完后，并不能立即生效，需要  **重启Apache**  
   **注：可以指定任意目录为根目录**

#### 5.4 网站部署

将我们制作好的网页**拷贝**到配置好的根目录下，浏览器访问127.0.0.1即可。

#### 5.5配置虚拟主机

在一台Web服务器上，我们可以通过配置虚拟主机，然后分别设定根目录，实现对多个网站的管理。

具体步骤如下：  

1、开启虚拟主机辅配置，在httpd.conf 中找到  
  
![](m_09.png)  

去掉前面的#号注释，开启虚拟主机配置

2、配置虚拟主机，打开conf/extra/httpd-vhosts.conf   

 ![](m_10.png) 
  
分别修改以下三项  

DocumentRoot "E:/www/example"  
ServerName "example.com "  
ServerAlias "www.example.com"  

其它项无需指定。

3、修改DNS（hosts）文件
打开C:\Windows\System32\drivers\etc\hosts
目录是固定的
![](m_11.png)  
注：修改hosts文件权限

4、重启Apache

5、浏览器访问www.example.com

## 6、PHP基础

>文件以.php后缀结尾，所有程序包含在<?php ** 这里是代码 ** ?>  
>避免使用中文目录和中文文件名
>php页面无法直接打开需要运行在服务器环境当中

#### 6.1、最简单的php程序

```php
<?php
	// 用来指定编码集
	header('Content-Type:text/html; charset=utf-8');
	/*这是一个最简单的php程序*/
	echo 'hello world!';
?>
```


#### 6.3、变量

1、变量以$开头 字母/数字/下划线 不能以数字开头  
2、大小写敏感（区分大小写）

```php
    // 声明一个变量$a并赋值为10
    $a = 10;
 	// 输出一个变量$a
    echo $a;
    // 声明一个变量$b并赋值为10
    $b = 10;
    // 输出一个变量$b
    echo $b;
    
    // 输出顺序是自上向下的
    // 相当于 js dcoument.write()
```

#### 6.3、数据类型

**字符型**  
```php
    $str = 'hello world!';
```

**整型**  
```php
    $num = 10;
```

**浮点型**  
```php
    $float = 10.5;
```

**布尔型**  
```php
    $bool = true;
```

**数组**
```php
    // Javascript 数组定义方式 var arr = [1, 2, 3]
    // arr[0]、arr[1]、arr[1]
    
    // PHP 是这样定义数组的
    // 这种方式叫做**索引数组**
    $arr = array(1, 2, 3);	
    
    // echo $arr[0];
    // echo $arr[2];
    
    // 定义方式和Javascript有区别，但是访问方式是一样的
```

```php
    // var obj = {name: itcast, age: 10}
    
    // PHP另一种定定数组的方式，所表达的意义和Javascript一样，
    // 只是语法格式不一样
    // 这种方式叫做**关联数组**
    $arr1 = array('name'=>'itcast', 'age'=>10);

    // echo $arr1['name'];
    // echo $arr1['age'];
```

**对象**  
```php
    // Javascript var obj = {name: itcast, age: 10}
    // PHP需要先创建一个类，下面就是创建过程
    class Person {
        public $name = 'itcast';

        public $age = 10;
    }

    $person = new Person;

    // PHP访问一个对象属性的语法是不一样的
    echo $person->name;

    // obj['name']; obj.name 不行
```

**NULL**  
```php
    //PHP中一种特殊的数据类型，表示空值，即表示没有为该变量设置任何值null(空值)不区分大小写，null和NULL是一样的。
```


**单引号&双引号区别： ** 单引号内部的变量不会执行双引号会执行
```php
    $name = '小明';

    echo 'name is $name';//输出 name is $name
    echo '<br>';
    echo "name is $name";//输出 name is 小明
```
索引数组、关联数组（了解即可）

#### 6.4、运算符&内容输出

    基本与Javascript语法一致  
    . 号表示字符串拼接符，Javascript中为+号
    

echo：输出简单数据类型，如字符串、数值
```php
    /**
    * 连接符
    * Javascript中用+号表示连接符
    * PHP中使用.点号
    */
    
    $hello = 'hello';
    
    $world = 'world!';
    
    // PHP连接符用.号
    echo $hello . ' ' .$world;
```

print_r()：输出复杂数据类型，如数组
```php
    $arr = array('itcast', '今年', '10岁了');
    
    // 只能输出简单类型
    echo $arr;

    // 可以打印数组，但是输出的是一个数组的结构
    print_r($arr);
```

var_dump()：输出详细信息，如对象、数组（了解）
```php
    $arr = array('itcast', '今年', '10岁了');
    
    // 只能输出简单类型
    echo $arr;

    // 输出详细信息
    var_dump($arr);
    
    $hello = 'hello';
    // 输出详细信息
    var_dump($hello);
```


#### 6.5、函数

基本与Javascript基本一致  

函数名对大小写不敏感  
默认参数（了解即可）  

```php
    // 1、PHP中函数不可以省略参数
	// 2、PHP可以设置默认参数
	
	function sayHello($name='web developer') {
		echo $name . '你好!';
	}

	sayHello();
```

#### 6.6、分支，循环语句
```php
    /**
     * 分支控制语句、循环语句
     * 与Javascript一样
     * foreach 数组遍历函数，类似Javascript中的 for in
     */
     $name = 'itcast1';
 
     if($name == 'itcast') {
        echo '我已经在' . $name . '学习';
     } else {
        echo '我还没有学习过编程';
     }
```

```php
    /**
     * 分支控制语句、循环语句
     * 与Javascript一样
     * foreach 数组遍历函数，类似Javascript中的 for in
     */
    // 索引数组
    $arr = array('itcast', '今年', '10岁了');
     
    // PHP函数，计算数组的长度
    $length = count($arr);
 
    // echo $length;
 
    // 和Javascript是一样的
    // for($i=0; $i<$length; $i++) {
    // 	echo $arr[$i];
    // }
 
    foreach($arr as $k=>$v) {
        echo $k . '~~~' . $v;
    };
 
    // 关联数组
    $arr1 = array('name'=>'itcast', 'age'=>'10');
 
    // 验证关联数组不可以按索引下标来访问
    // echo $arr1[0];
 
     //PHP遍历一个关联数组
     foreach($arr1 as $key=>$val) {
        echo $key . '~~~' . $val;
     }
     
     // 实际开发都是用foreach来遍历数组的
```

#### 6.7、表单处理

表单name属性的是用来提供给服务端接收所传递数据而设置的 
   
表单action属性设置接收数据的处理程序

表单method属性设置发送数据的方式

当上传文件是需要设置 enctype="multipart/form-data"，且只能post方式

$_GET接收 get 传值

$_POST接收 post 传值

$_FILES接收文件上传  

    move_uploaded_file($_FILES['image']['tmp_name'], 'test.jpg');

```html
<form action="login.php" method="post">
    <div class="row">
        <label>用户：</label><input type="text" name="username"/>
    </div>
    <div class="row">
        <label>密码：</label><input type="<PASSWORD>" name="password"/>
    </div>
    <div class="row">
        <input type="submit" value="登录"/>
    </div>
</form>
<form action="login.php" method="post" enctype="multipart/form-data">
    <div class="row">
        <label>图片：</label><input type="file" name="image"/>
    </div>
    <div class="row">
        <input type="submit" value="上传"/>
    </div>
</form>
```


    
#### 6.8、文件导入&&常用php函数

```PHP
    include '07.form.html';

    require '07.form.php'
```

```php
    $array = array(
        'username'=>'itcast',
        'password'=>'<PASSWORD>'
    );

    echo '获取数组的长度：'.count($array);
    echo '<br>';
    echo '判断是否在数组中：'.in_array('itcast',$array);
    echo '<br>';
    echo '检测数组中是否存在key：'.array_key_exists('username',$array);
    echo '<br>';
```



#### 6.9、应用实例

1、用户登录  

```html
    <form action="login.php" method="post">
        <div class="row">
            <label>用户：</label><input type="text" name="username"/>
        </div>
        <div class="row">
            <label>密码：</label><input type="<PASSWORD>" name="password"/>
        </div>
        <div class="row">
            <input type="submit" value="登录"/>
        </div>
    </form>
```

```php
<?php
    header('Content-Type:text/html; charset=utf-8');

    /*数据库当中的数据*/
    $userInfo = array(
        'username'=>'itcast',
        'password'=>'<PASSWORD>'
    );

    /*拿到提交过来的数据*/
    $username = $_POST['username'];
    $password = $_POST['password'];

    /*去匹配数据库当中的数据*/
    if($userInfo['username'] == $username && $userInfo['password'] == $password){
        header('refresh:0;url=jdMsite/');
    }else{
        header('refresh:0;url=login.html');
    }
?>
```

2、动态网站   
   
**京东首页**
```php
    <?php
        header('Content-Type:text/html; charset=utf-8');
    
        $product_list = array(
            array(
            'imgUrl'=>'images/detail01.jpg',
            'newPrice'=>'15.00',
            'oldPrice'=>'19.00'
            ),
            array(
            'imgUrl'=>'images/detail02.jpg',
            'newPrice'=>'133.00',
            'oldPrice'=>'234.00'
            ),
            array(
            'imgUrl'=>'images/detail01.jpg',
            'newPrice'=>'340.00',
            'oldPrice'=>'1432.00'
            )
        );
    
        include 'index.html';
    ?>
```
```php
    <!--掌上秒杀的内容是会更新的而且是后台更新-->
    <ul class="sk_product">
        <?php foreach($product_list as $key => $val){ ?>
            <li>
                <a href="#"><img src="<?php echo $val['imgUrl'] ?>" alt=""/></a>
                <p class="new_price">&yen;<?php echo $val['newPrice'] ?></p>
                <p class="old_price">&yen;<?php echo $val['oldPrice'] ?></p>
            </li>
        <?php } ?>
    </ul>
```
**京东分类**

```php
<?php
    header('Content-Type:text/html; charset=utf-8');

    $category = array(
        '热门推荐',
        '潮流女装',
        '品牌男装',
        '内衣配饰',
        '家用电器',
        '电脑办公',
        '手机数码',
        '母婴频道',
        '图书',
        '家居家纺',
        '居家生活',
        '家具建材',
        '热门推荐',
        '潮流女装',
        '品牌男装',
        '内衣配饰',
        '家用电器',
        '电脑办公',
        '手机数码',
        '母婴频道',
        '居家生活',
        '手机数码',
        '母婴频道',
        '图书',
        '家居家纺',
        '居家生活',
        '潮流女装',
        '家具建材',
        '热门推荐',
        '潮流女装',
    );

    //echo count($category);

    include 'category.html';
?>
```
```php
    <!--左侧分类-->
    <div class="jd_cate_left">
        <ul>
            <?php foreach($category as $key=>$val){ ?>
                <li class="<?php echo $key==1?'now':'' ?>"><a href="javascript:;"><?php echo $val ?></a></li>
            <?php } ?>
        </ul>
    </div>
```


## 7、网络传输协议

#### 7.1、常见协议

1、HTTP、HTTPS 超文本传输协议 

2、FTP 文件传输协议

3、SMTP 简单邮件传输协议

    
#### 7.2、http协议

超文本传输协议（HTTP，HyperText Transfer Protocol) 网站是基于HTTP协议的，
例如网站的图片、CSS、JS等都是基于HTTP协议进行传输的。

HTML  Hypertext Markup Language

HTTP协议是由从客户机到服务器的请求(Request)和从服务器到客户机的响应(Response)进行了约束和规范。

即HTTP协议主要由请求和响应构成。

![](m_13.png)
 
常用请求方法 **POST**、**GET**、PUT、DELETE

我们通过浏览器插件 FireFox httpFox  调试。ctrl shift f2 调用


##### 7.2.1、请求和请求报文

请求由客户端发起，其规范格式为：请求行、请求头、请求主体。

- **1、请求行**
 
由请求方式、请求URL和协议版本构成

    GET /day01/code/login.php?username=123&password=123 HTTP/1.1
    
    POST /day01/code/login.php HTTP/1.1

- **2、请求头**

Host：localhost请求的主机

Cache-Control：max-age=0控制缓存

Accept：*/* 接受的文档MIME类型

User-Agent：很重要

Referer：从哪个URL跳转过来的

Accept-Encoding：可接受的压缩格式

If-None-Match：记录服务器响应的ETag值，用于控制缓存

此值是由服务器自动生成的

If-Modified-Since：记录服务器响应的Last-Modified值

此值是由服务器自动生成的

- **3、请求主体**

即传递给服务端的数据

注：当以post形式提交表单的时候，请求头里会设置

Content-Type: application/x-www-form-urlencoded，以get形式当不需要


##### 7.2.2、 响应和响应报文

响应由服务器发出，其规范格式为：状态行、响应头、响应主体。

- **1、状态行**
 
由协议版本号、状态码和状态信息构成
    HTTP/1.1 200 OK

- **2、响应头**

Date：响应时间

Server：服务器信息

Last-Modified：资源最后修改时间 由服务器自动生成

ETag：资源修改后生成的唯一标识

由服务器自动生成

Content-Length：响应主体长度

Content-Type：响应资源的类型

- **3、响应主体**

即服务端返回给客户端的内容；

- 状态码
 
常见的有200代表成功、304文档未修改、403没有权限、404未找到、500服务器错误

![](get请求.png)
![](post请求.png)

## 8、AJAX编程

>即 Asynchronous [e'sɪŋkrənəs] Javascript And XML，
AJAX 不是一门的新的语言，而是对现有技术的综合利用。
>本质是在HTTP协议的基础上以异步的方式与服务器进行通信。
>

#### 8.1、 异步

指某段程序执行时不会阻塞其它程序执行，其表现形式为程序的执行顺序不依赖程序本身的书写顺序，相反则为同步。

**其优势在于不阻塞程序的执行，从而提升整体执行效率。**

XMLHttpRequest可以以异步方式的处理程序。

#### 8.2、 XMLHttpRequest

浏览器内建对象，用于在后台与服务器通信(交换数据) ，
由此我们便可实现对网页的部分更新，而不是刷新整个页面。

```javascript
    /*js 内置的 http 请求对象  XMLHttpRequest*/

    /*1.怎么使用 这个内置对象*/
    var xhr = new XMLHttpRequest;

    /*2.怎么样去组请求*/
    /*请求的行*/
    xhr.open('post','01.php');

    /*请求头*/
    //get  没有必要设置
    //post 必须设置 Content-Type: application/x-www-form-urlencoded
    xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');

    /*请内容*/
    /*3.发送请求*/
    xhr.send("name=xjj&age=10");
```

##### 8.2.1、请求

HTTP请求3个组成部分与XMLHttpRequest方法的对应关系

1、请求行

    xhr.open('post','01.php');
    
2、请求头

    xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
    get请求可以不设置
    
3、请求主体

    xhr.send("name=xjj&age=10");
    get可以传空
    
**注意书写顺序**
    
    
##### 8.2.2、响应
HTTP响应是由服务端发出的，作为客户端更应关心的是响应的结果。

HTTP响应3个组成部分与XMLHttpRequest方法或属性的对应关系。

由于服务器做出响应需要时间（比如网速慢等原因），所以我们需要监听服务器响应的状态，然后才能进行处理。

```javascript
    if(xhr.readyState == 4 && xhr.status == 200){
        console.log('ok');
        console.log(xhr.responseText);
        /*把内容渲染在页面当中*/
        document.querySelector('#result').innerHTML = xhr.responseText;
    }
```
    **readyState**
    0：请求未初始化（还没有调用 open()）。  
    1：请求已经建立，但是还没有发送（还没有调用 send()）。  
    2：请求已发送，正在处理中（通常现在可以从响应中获取内容头）。  
    3：请求在处理中；通常响应中已有部分数据可用了，但是服务器还没有完成响应的生成。  
    4：响应已完成；您可以获取并使用服务器的响应了。

onreadystatechange是Javascript的事件的一种，其意义在于监听XMLHttpRequest的状态

1、获取状态行（包括状态码&状态信息）

    xhr.status 状态码
    
    xhr.statusText 状态码信息
    
 
2、获取响应头

    xhr.getResponseHeader('Content-Type');
    xhr.getAllResponseHeaders();
   
    
3、响应主体  
  
    xhr.responseText
    xhr.responseXML
    
我们需要检测并判断响应头的MIME类型后确定使用request.responseText或者request.responseXML
    
##### 8.3.3、API详解

    xhr.open() 发起请求，可以是get、post方式
    xhr.setRequestHeader() 设置请求头
    xhr.send() 发送请求主体get方式使用xhr.send(null)
    xhr.onreadystatechange = function () {} 监听响应状态
    xhr.status表示响应码，如200
    xhr.statusText表示响应信息，如OK
    xhr.getAllResponseHeaders() 获取全部响应头信息
    xhr.getResponseHeader('key') 获取指定头信息
    xhr.responseText、xhr.responseXML都表示响应主体
    
 注：GET和POST请求方式的差异（面试题）
 
 1、GET没有请求主体，使用xhr.send(null)
 
 2、GET可以通过在请求URL上添加请求参数
 
 3、POST可以通过xhr.send('name=itcast&age=10')
 
 4、POST需要设置
  
 5、GET效率更好（应用多）
 
 6、GET大小限制约4K，POST则没有限制
 
 问题？如何获取复杂数据呢？

#### 8.3、 XML

1、必须有一个根元素  
2、不可有空格、不可以数字或.开头、大小写敏感  
3、不可交叉嵌套  
4、属性双引号（浏览器自动修正成双引号了）  
5、特殊符号要使用实体  
6、注释和HTML一样  
虽然可以描述和传输复杂数据，但是其解析过于复杂并且体积较大，所以实现开发已经很少使用了。
   
```xml
<?xml version="1.0" encoding="UTF-8"?>
<root>
    <arrayList>
        <array>
            <src>images/banner.jpg</src>
            <newPirce>12.00</newPirce>
            <oldPrice>30.00</oldPrice>
        </array>
        <array>
            <src>images/banner.jpg</src>
            <newPirce>12.00</newPirce>
            <oldPrice>30.00</oldPrice>
        </array>
    </arrayList>
</root>
```
```php
<?php 
    header('Content-Type:text/xml;charset=utf-8');
    /*以xml格式传输数据的时候要求响应内容格式是   text/xml*/

    /*file_get_contents 获取文件内容*/
    $xml = file_get_contents('01.xml');

    /*输出xml内容*/
    echo $xml;
?>
```
```javascript
var xhr = new XMLHttpRequest;
xhr.open('get','01.php');
xhr.send(null);
xhr.onreadystatechange = function(){
    if(xhr.status == 200 && xhr.readyState == 4){
        /*获取到XML格式内容  放回的是DOM对象  document*/
        var xml = xhr.responseXML;
        /*通过选着器可以获取到xml的数据*/
        console.log(xml.querySelectorAll('array')[0].querySelector('src').innerHTML);
    }
}
```

#### 8.4、 JSON

即 JavaScript Object Notation，另一种轻量级的文本数据交换格式，独立于语言。

1、数据在名称/值对中  
2、数据由逗号分隔(最后一个健/值对不能带逗号)  
3、花括号保存对象方括号保存数组  
4、使用双引号  

```JSON
[
  {"src":"images/detail01.jpg","oldPrice":"10.12","newPrice":"130.00"},
  {"src":"images/detail02.jpg","oldPrice":"1.00","newPrice":"11.00"},
  {"src":"images/detail03.jpg","oldPrice":"100.00","newPrice":"1000.00"}
]
```

JSON数据在不同语言进行传输时，类型为字符串，不同的语言各自也都对应有解析方法，需要解析完成后才能读取

**1、PHP解析方法**  
json_encode()、json_decode()  
```PHP
<?php 
    header('Content-Type:text/html;charset=utf-8');
    /*以json格式传输数据的时候要求响应内容格式是   application/json*/
    /*注意也可以不设置  但是这遵循的一个规范*/

    /*file_get_contents 获取文件内容*/
    $json = file_get_contents('01.json');

    /*输出json内容*/
    echo $json;
    echo '<br><br>';

    $array = array(
        array('src'=>'images/detail01.jpg','newPrice'=>'12.00','oldPrice'=>'455.00'),
        array('src'=>'images/detail02.jpg','newPrice'=>'65.00','oldPrice'=>'878.00'),
        array( 'src'=>'images/detail01.jpg','newPrice'=>'100.00','oldPrice'=>'1000.00')
    );

    /*将php数组转化成json字符*/
    $json_array = json_encode($array);
    echo $json_array;
    echo '<br><br>';

    /*将json字符转化成php数组*/
    $array_json = json_decode($json_array);
    echo $array_json;
    echo '<br><br>';
?>
```  
**1、Javascript 解析方法**  
JSON对象  JSON.parse()、JSON.stringify()；  
JSON兼容处理json2.js  
总结：JSON体积小、解析方便且高效，在实际开发成为首选。
```javascript
    var xhr = new XMLHttpRequest;
    xhr.open('get','01.php');
    xhr.send(null);
    xhr.onreadystatechange = function(){
        if(xhr.status == 200 && xhr.readyState == 4){
            /*获取仅仅是字符串*/
            var text = xhr.responseText;
            
            /*需要把字符串转化成JSON对象*/
            var json_obj = JSON.parse(text);
            console.log(json_obj);
    
            /*我们也可以把JSON对象转化成字符串*/
            var json_str = JSON.stringify(json_obj);
            console.log(json_str);
        }
    }
```



#### 8.5、 兼容性

**关于IE的兼容方面，了解即可。**
```javascript
    function XHR() {
        var xhr;
        try {
            xhr = new XMLHttpRequest();
        }
        /*如果 try内的程序运行错误  抛出异常  捕捉异常  上面程序当中运行的错误*/
        catch(e) {
            /*在不同的IE版本下初始  ActiveXObject  需要传入的标识*/
            var IEXHRVers =["Msxml3.XMLHTTP","Msxml2.XMLHTTP","Microsoft.XMLHTTP"];

            for (var i=0;i<IEXHRVers.length;i++) {
                try {
                    xhr = new ActiveXObject(IEXHRVers[i]);
                }
                catch(e) {
                    /*如果出现错误的时候  停止当次的循环*/
                    continue;
                }
            }
        }
        return xhr;
    }
```

#### 8.6、 封装ajax工具函数

```javascript
/**
 * ITCAST WEB
 * Created by zhousg on 2016/5/24.
 */
/*
 * 1. 请求的类型                type    get post
 * 2. 请求地址                  url
 * 3. 是异步的还是同步的         async   false true
 * 4. 请求内容的格式            contentType
 * 5. 传输的数据                data    json对象
 *
 * 6.响应成功处理函数           success   function
 * 7.响应失败的处理函数         error     function
 *
 * 这些都是动态参数  参数对象  options
 * */

/*封装一个函数*/
window.$ = {};
/*申明一个ajax的方法*/
$.ajax = function(options){

    if(!options || typeof options != 'object'){
        return false;
    }

    /*请求的类型*/
    var type = options.type || 'get';/*默认get*/
    /*请求地址 */
    var url = options.url || location.pathname;/*当前的地址*/
    /*是异步的还是同步的 */
    var async = (options.async === false)?false:true;/*默认异步*/
    /*请求内容的格式 */
    var contentType = options.contentType || "text/html";

    /*传输的数据 */
    var data = options.data || {};/*｛name:'',age:''｝*/
    /*在提交的时候需要转成 name=xjj 这种格式*/

    var dataStr = ''/*数据字符串*/

    for(var key in data){
        dataStr += key+'='+data[key]+'&';
    }

    dataStr = dataStr && dataStr.slice(0,-1);

    /*ajax 编程*/
    var xhr = new XMLHttpRequest();

    /*请求行*/
    /*(type=='get'?url+'?'+dataStr:url)判断当前的请求类型*/
    xhr.open(type,(type=='get'?url+'?'+dataStr:url),async);

    /*请求头*/
    if(type == 'post'){
        xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
    }

    /*请求主体*/
    /*需要判断请求类型*/
    xhr.send(type=='get'?null:dataStr);

    /*监听响应状态的改变  响应状态*/
    xhr.onreadystatechange = function(){
        /*请求响应完成并且成功*/
        if(xhr.readyState == 4 && xhr.status == 200){
            /*success*/
            var data = '';
            var contentType = xhr.getResponseHeader('Content-Type');
            /*如果我们服务器返回的是xml*/
            if(contentType.indexOf('xml') > -1){
                data = xhr.responseXML;
            }
            /*如果我们的服务器返回的是json字符串*/
            else if(contentType.indexOf('json') > -1){
                /*转化json对象*/
                data = JSON.parse(xhr.responseText);
            }
            /*否则的话他就是字符串*/
            else{
                data = xhr.responseText;
            }

            /*回调 成功处理函数*/

            options.success && options.success(data);
        }
        /*计时请求xhr.status不成功  他也需要的响应完成才认作是一个错误的请求*/
        else if(xhr.readyState == 4){
            /*error*/
            options.error && options.error('you request fail !');

        }

    }
}
$.post = function(options){
    options.type = 'post';
    $.ajax(options);
}
$.get = function(options){
    options.type = 'get';
    $.ajax(options);
}



```

#### 8.7、 jquery的ajax

- jQuery为我们提供了更强大的Ajax封装
- $.ajax({}) 可配置方式发起Ajax请求
- $.get() 以GET方式发起Ajax请求
- $.post() 以POST方式发起Ajax请求
- $('form').serialize() 序列化表单（即格式化key=val&key=val）
- url 接口地址
- type 请求方式
- timeout 请求超时
- dataType 服务器返回格式
- data 发送请求数据
- beforeSend: function () {} 请求发起前调用
- success 成功响应后调用
- error 错误响应时调用
- complete 响应完成时调用（包括成功和失败）
- jQuery Ajax介绍
- http://www.w3school.com.cn/jquery/jquery_ref_ajax.asp


#### 8.8、 案例练习

1、Loading状态  
2、禁止重复提交  
3、表单处理  
4、数据验证  
接口化开发  
请求地址即所谓的接口，通常我们所说的接口化开发，其实是指一个接口对应一个功能，
并且严格约束了请求参数和响应结果的格式，这样前后端在开发过程中，可以减少不必要的讨论，
从而并行开发，可以极大的提升开发效率，另外一个好处，当网站进行改版后，服务端接口只需要进行微调。


**具体参考代码**

## 9、模版引擎

#### 9.2、 artTemplate 简介语法模板

```HTML
<script src="dist/template.js"></script>
```
下载(https://raw.github.com/aui/artTemplate/master/dist/template.js)

编写模版

    <script id="test" type="text/html">
    <h1>{{title}}</h1>
    <ul>
        {{each list as value i}}
            <li>索引 {{i + 1}} ：{{value}}</li>
        {{/each}}
    </ul>
    </script>
    
渲染数据
    
    var data = {
        title: '标签',
        list: ['文艺', '博客', '摄影', '电影', '民谣', '旅行', '吉他']
    };
    var html = template('test', data);
    document.getElementById('content').innerHTML = html;
    
简介语法    
    
    {{if admin}}
        {{include 'admin_content'}}
    
        {{each list}}
            <div>{{$index}}. {{$value.user}}</div>
        {{/each}}
    {{/if}}
    
    

#### 9.1、 artTemplate 原生 js 模板语法版

**使用**

在页面中引用模板引擎：

```HTML
<script src="dist/template-native.js"></script>
```
下载(https://raw.github.com/aui/artTemplate/master/dist/template-native.js)

**表达式**

    <% 与 %> 符号包裹起来的语句则为模板的逻辑表达式。

**输出表达式**

对内容编码输出：

    <%=content%>
    
不编码输出：

    <%=#content%>
    
编码可以防止数据中含有 HTML 字符串，避免引起 XSS 攻击。

**逻辑**

支持使用 js 原生语法

    <h1><%=title%></h1>
    <ul>
        <%for(i = 0; i < list.length; i ++) {%>
            <li>条目内容 <%=i + 1%> ：<%=list[i]%></li>
        <%}%>
    </ul>
模板不能访问全局对象，公用的方法请参见文档 辅助方法 章节
模板包含表达式

用于嵌入子模板。

    <% include('template_name') %>
    
子模板默认共享当前数据，亦可以指定数据：

    <% include('template_name', news_list) %>
    
辅助方法

使用template.helper(name, callback)注册公用辅助方法，例如一个基本的 UBB 替换方法：

    template.helper('$ubb2html', function (content) {
        // 处理字符串...
        return content;
    });
    
模板中使用的方式：

    <% $ubb2html(content) %>

## 10、同源&跨域

####10.1	同源

同源策略是浏览器的一种安全策略，所谓同源是指，域名，协议，端口完全相同。

####10.2	跨域

不同源则跨域
例如http://www.example.com/

    http://api.example.com/detail.html  不同源 域名不同  
    https//www.example.com/detail.html   不同源 协议不同  
    http://www.example.com:8080/detail.html	不同源	端口不同  
    http://api.example.com:8080/detail.html	不同源	域名、端口不同  
    https://api.example.com/detail.html	不同源	协议、域名不同  
    https://www.example.com:8080/detail.html	不同源	端口、协议不同  
    http://www.example.com/detail/index.html	同源	只是目录不同  

####10.3	跨域方案（课外拓展）

1、顶级域名相同的可以通过domain.name来解决，即同时设置 domain.name = 顶级域名（如example.com）  
2、document.domain + iframe  
3、window.name + iframe  
4、location.hash + iframe  
5、window.postMessage()  

参考资料
http://rickgray.me/2015/09/03/solutions-to-cross-domain-in-browser.html


#### 10.4、 jsonp

>JSON with Padding

**1、原理剖析**

其本质是利用了<script src=""></script>标签具有可跨域的特性，
由服务端返回一个预先定义好的Javascript函数的调用，并且将服务器数据以该函数参数的形式传递过来，
此方法需要前后端配合完成。

    <!--
    当我们用script标签去加载的时候  会把内容解析成js去执行
    -->
    <script>
        function fuc(data){
            console.log(data.name);
        }
    </script>
    <script src="http://www.guangzhou.com/api.php?callback=fuc"></script>
    




## 11、综合案例

#### 11.1、瀑布流案例（必须掌握）

#### 11.2、天气接口（必须掌握）
接口地址
http://developer.baidu.com/map/carapi-7.htm

    url: 'http://api.map.baidu.com/telematics/v3/weather?output=json&ak=<KEY>',

![](jk.png)

#### 11.3、省级联动（课外拓展）<file_sep>## 词法作用域
- 欺骗词法
  + evel
    + 如果词法作用域完全由写代码期间函数所声明的位置来定义，怎么修改词法作用域呢？
    + evel();方法，传入一个字符串，可以把字符串当js代码解析执行。
    + 但在严格模式下，eval（）有自己的词法作用域，并不能修改其所在的作用域。
    + new Function（）也是一样，前面的参数是这个函数的形参，最后一个参数可以是字符串，会动态解析执行。比eval（）略显安全，但要尽量避免使用。
    + 这两个方式都很消耗性能，但能带来的好处很小。
  + with 
    + with通常被当成重复引用同一个对象中的多个属性的快捷方式。
    + with(){}方法本质是传入一个对象，在with中生成一个词法作用域。可以直接使用这个对象的属性。不用对象.属性的方式。
    + 但如果这个对象没有这个属性，那么with会产生LHS查找，于是把这个属性声明到全局去了。
    + with在严格模式下完全禁止。
```js
var obj={name:"zsn",age:10};
with(obj){console.log(name)};//"zsn"
with(obj){ gender="男"};
console.log(obj.gender)// undefined因为obj没有gender属性，于是属性声明到了全局。 
console.log(gender) //男 发生了内存泄漏
```
- 性能
  + 如果上述改变词法作用域的方式能实现更复杂的功能，并且代码更具有扩展性，是不是非常好呢？
  + 但实际情况并不是这样的，js引擎会在编译期间进行数项的性能优化，其中有些优化依赖于词法进行静态分析。并且先确定变量和函数定义的位置，才能在执行过程中快速找到标识符。
  + 但是引擎在代码中发现了eval和with，它只能简单假设关于标识符判断是无效的，因为eval在词法分析阶段无法判断传入声明代码。也没法知道with创建新词法作用域的对象内容到底是声明。
  + 最悲观的情况就是出现了上述欺骗词法，所以的编译期间的优化都是无效的，最简单的做法就是不优化。
  + 如果欺骗词法出现过多，即使引擎把这种悲观的情况控制在最小范围内，也没有办法避免失去了编译期间的数项性能优化，代码运行变慢的事实。
- 小结
  + 编译的词法分析阶段基本能够知道全部标识符在哪里以及如何声明的，从而能预测在执行期间如何对它们进行查找。
  + eval和with都是在运行期间修改当前词法作用域和生成一个新的词法作用域。这两个机制的副作用是在编译期间引擎无法对其作用域进行优化，因为引擎只能谨慎的认为这样的优化是无效的。使用这其中的如何一个机制都将导致代码运行变慢，最好不要使用。

 ###  函数作用域和块作用域
 - 隐藏内部实现
   + 最小特权原则，这个原则指在软件设计中，应该极小限度暴露必要内容，而将其它内容隐藏起来，比如某个模块或对象设计的API。可以用函数来达到隐藏代码的目的。
   + 规避冲突
      + 全局命名空间：第三方库通常会在全局作用域中声明一个名字足够独特的变量，通常是个对象，这个对象被称为库的命名空间。所有需要在外界的功能都是它的属性，而不是将自己的标识符暴露在顶级词法作用域中。
      + 模块管理：如何库都无法将标识符加入到全局作用域，  而是通过依赖管理器的机制将库的标识符显示地导入到另一个特点地作用域中。
  - 函数作用域
   + 区分函数声明和函数表达式最简单地方式是看关键字function出现在声明中地位置，如果function是声明中的第一个词，那么就是一个函数声明，否则是一个函数表达式。函数声明和函数表达式的区别就是它们的标识符将会绑定于何处。
   + 匿名函数的三个缺点:匿名函数在栈追踪中不会显示出有意义的函数名，使得调试困难；如果没有函数名，当函数需要引用自身只能用过期的arguments.callee；匿名函数省略了对于代码可读性很重要的函数名。最佳实践： 函数表达式非常强大且有用，匿名和具名区别并不会有区别，解决上述问题是加一个函数名。
   + (function(){})()第一个括号将这个函数变成了一个表达式，第二个括号将这个函数执行了。这种模式有一个专业术语：IIFE。
   相对于这个传统模式，还有一种改进模式：(function(){}())。两者形式功能上是一样的。
   + 块级作用域：try{}catch(err){}err只能在catch代码段中访问，并没有声明到全局。
     + function b(a){console.log(a)}; b(3);这种情况下在全局访问a为3，说明函数的形参也被挂到了全局中。
   + let ecmascript6新的关键字。能把声明的变量绑定在当前 {}中，如if（true）{}，而且在当前作用域不会像var一样对变量进行提升。只能在声明后访问，不然会报错。 
   + const常量除了赋值后不能改变外，其它都和let一样。注意，变量定义后不能再用常量修饰。
### 提升
 - var a=2;其实是两个声明，var a; a=2;第一个声明是在编译阶段执行的，第二个声明是在代码执行阶段执行的。变量声明提升到作用域顶部，赋值留在原地。
 - 函数表达式即使给命名函数，也不会发生函数提升。
 ``` js
 f(); //TypeError
 fn(); //fn is not defined
 var f=function fn(){};
 ```
 - 函数优先
   + 函数声明和变量声明都会被提升，但函数声明优先于变量声明，如果函数声明名称和变量声明名称一样，重复出现的声明，后者会直接被忽略。
   + 同名函数，后面出现的会覆盖前面的。
### 作用域闭包
   + 当函数可以记住并访问所在词法作用域时，就产生了闭包。
   + 就是在函数声明的词法作用域外使用它，闭包使得函数可以继续访问定义时的词法作用域。
```js
function foo(){ 
     var a=2; 
     function baz(){ console.log(a)};
     bar(baz)
     };
function bar(fn){fn()};
foo();//2
```
   + 间接传递参数
```js
  var fn;
  function foo(){ 
      var a=2; 
      function baz(){ console.log(a)}; fn=baz;
      };
      function bar(){fn()}; 
      foo();
      bar();
```   
+ 无论通过何种手段将内部函数传递到所在词法作用域外，它都会持有对原始作用域的引用，无论在何处使用这个函数都会使用闭包。
+ 无论是在定时器，事件监听，ajax等等，只要使用了回掉函数，实际上就是使用了闭包。因为回调函数可以访问到其声明的词法作用域。
+ 自调用函数的确创建了闭包，并且也是最常用来创建可以被封闭起来闭包的工具，即使本身并不会使用闭包。
+ 重返块级作用域:块级作用域和闭包的完美结合
```js
for(let i=0;i<5;i++){ 
    setTimeout(function(){console.log(i)},1000*i)
    }
 ```   
+ 模块(单利模式)通过模块实例内部保留对公共API的引用，可以从内部对模块实例进行修改，比如添加或删除方法和属性，以及修改它们的值。
```js 
var foo=(function(id){ 
    function change(){ publicApi.iden=identity2; } 
    function identity1(){console.log(id) } 
    function identity2(){ console.log(id.toUpperCase())}   
    var publicApi={iden:identity1,change:change };
    return publicApi;  
    })("dfsdds")
    foo.iden() //dfsdds
    foo.change()
    foo.iden() //DFSDDS
````
+ 现代模块机制:它符合前面模块模式的特点，为函数定义引入包装函数，并保证它的返回值和模块的API保持一致。
````js
   var MyModules=(function(){
      var moudules=[];
       function define(name,deps,impl)
       {
           //name函数名
           //deps依赖的函数名组成的数组
           //impl函数实体
           for (var i = 0; i < deps.length; i++) {
               //从所有模块里获取当前模块依赖的模块执行的结果
               console.log(deps[i])
               //deps[i]其实就是让这个模块的名称指向了对应的模块moudules[deps[i]]
               //引用类型复制的时值的指针
               deps[i]=moudules[deps[i]];
           }
          //带入参数，执行模块，把结果存入数组，
           moudules[name]= impl.apply(impl,deps[i]);
       }
       function get(name){
           return moudules[name];
       }
       return {
           define:define,
            get:get
       }
   })();
   MyModules.define("bar",[],function(){
       function hello(who){
           return "Let me introduce"+who;
       }
       return {
           hello:hello
       }
   })
   MyModules.define("foo",["bar"],function(){
       var hungry="hippo";
       function awesome(){
           console.log(bar.hello(hungry).toUpperCase());
       }
       return {
           awesome:awesome
       };
   })

    var bar=MyModules.get("bar");
    var foo=MyModules.get("foo");

   console.log(bar.hello("tt"));

   foo.awesome();
````

+ 未来模块机制 通过模块系统加载时，ES6会将文件当作独立的模块来处理。
  + 基于函数的模块并不是一个能被稳定识别的模式（编译器无法识别的），它们的API语义只有在运行时才会被考虑进来。因此可以在运行中修改一个模块的API。
  + 相比之下，es6模块API更加稳定（API不会在运行时进行修改）。编译期会检查导入的模块API是否存在，如果API不存在，编译器会在运行时抛出一个或多个“早期”错误。而不会像无偿一样在运行期采用动态的解决方案。

````js
module foo from "foo";
foo.sayHello();
````
+ 模块模式主要有两个特征：（1）为创建内部作用域而调用了一个包装函数；（2）包装函数的返回值必须至少包括一个对内部函数的引用，这样就会创建涵盖整个包装函数内部作用域的闭包。

### 附录A 动态作用域
````js
  function foo(){console.log(a)} 
  function bar(){var a=3; foo();} 
  var a=2;
  bar();//2
  //因为foo虽然是在bar函数内部调用的，但它定义的作用域是在全局作用域，所以只能取到全局的a。（闭包）
````
  + 如果javascript具有动态作用域，理论上，上面代码输出3，而不是2。在动态作用域中，因为在bar中调用了foo，所以foo就可以访问bar的作用域，当foo中没有a，就会顺着作用域找到bar中的a。
  + 实际上javascript不具备动态作用域，它只有词法作用域。但this机制某种程度上像动态作用域。
  + 主要区别：词法作用域是在写代码或则说定义时确定的，而动态作用域是在运行时决定的。（this也是关注函数如何调用）词法作用域关注函数在何处声明，而动态作用域关注函数在何处调用。
### 附录B 块级作用域的替代方案
+ 考虑下面的代码在es5怎么实现
````js
{
  let a=2;
  console.log(a);//2
}
console.log(a);//ReferenceError

//如何能在es5中实现呢？可以考虑catch 其实自调用函数也是可以的
try{throw 2;}catch(a){console.log(a)};
console.log(a)//ReferenceError
````
 + Traceur 
   + Google维护着一个名为Traceur的项目，该项目可以将ES6转换成兼容es6之前的环境（大部分是es5，但不是全部）
   + Traceur转换后的代码片段
   ````js
   {
     try{throw undefined;}
     catch(a){
       a=2;
       console.log(a);
       };
   }
   ````
+ 隐式和显示作用域
   + 用let将变量附加在一个已经存在的块级作用域上的行为是是隐式的。在开始和修改中的过程中，如果没有密切关注哪些块作用域中有绑定的变量，并且习惯性的移动这些块或则将其包含在其它块中，将导致代码混乱。
   + 两种显示创建块
   ````js
   if(foo){
     {//《--显示的块
       let bar=foo*2;
       console.log(bar);
     }
   }

   //同隐式劫持一个已经存在的作用域不同，let声明创建一个一个显示的作用域并与其进行绑定。显示作用域不仅更加突出，在代码重构也表现的更加健壮。
   //这样更任意判断变量属于哪个作用域。但下面let声明并不是包含在es6中。推荐使用上面一种。
   let(a=2){
     console.log(a);2
   }
    console.log(a);RefereceError
   ````  
+ 性能
   + try/catch性能确实很差，但Traceur团队已经要求Chrome对它的性能进行改进了，他们有充分的理由进行改进。技术上不是限制try/catch性能的原因，它也没有理由一直差下去。IIFE和try/catch并不是完全等价的，其中的this，return，break，continue都会发生变化。所以IIFE并不是一个很好的解决方案。
   +IIFE（自调用函数）
 
### 附录C this词法
 + es6的箭头函数和普通函数在处理this的指向是不一样的，箭头函数会把当前词法作用域覆盖this本身的值。(也可以使用绑定或则变量selt存储当前词法作用域的this进行使用)
 ````js
     var obj={
        id:"tttt",
        cool:function coolFn(){
            var selt=this;
            setTimeout(() => {
                console.log(this.id);
            },100)
        }
    }
    obj.cool();
````

## this和对象原型
### 关于this
+ 为什么要使用this？可以把显示传参替代为隐式传参。this提供了一个更加优雅的方式来隐式“传递”一个对象的引用。
````js
    function identify(){
       return this.name.toUpperCase();
   }
   function speak(){
       return "hello i`m "+identify.call(this);
   }
var obj={name:"zsn"}
console.log(identify.call(obj))
console.log(speak.call(obj))
````
+ 两者this的误解：1）指向函数本身。2）this指向函数的作用域，虽然在某些情况下是正确的，但其它情况下确实错误的。
+ this到底是什么：this是在运行时进行绑定的，并不是编译时绑定，它的上下文取决于函数调用的各种条件，this的绑定和函数声明的位置没有任何关系，取决于函数的调用方式。
   - 当一个函数被调用时，会创建一个活动记录（也称执行上下文）。这个记录会包含函数在哪里调用（调用栈），函数的声明方法，传入的参数等信息。this也是记录中的一个属性，会在函数执行的过程中用到。
### this的全面解析
  + 函数this的四种绑定
    - 默认绑定：函数不带任何修饰进行绑定，只能是默认绑定，绑定的对象为window；在严格模式下默认绑定的对象为undefined。
    - 隐式绑定：调用位置是否有上下文对象，或则说被某个对象拥有或包含。调用位置会使用obj的上下文，可以说函数被调用的对象“拥有”或则“包含”它。
    对象属性引用链只有最顶层或则说最后一层会影响调用位置。
    ````js
    function foo(){
      console.log(this.a);
    }
    var obj={
      a:2,
      foo:foo
    }
    obj.foo();
    ````

    + 隐式丢失：隐式绑定的函数会丢失对象，也就是会回到默认绑定。
    ````js
     function foo(){
      console.log(this.a);
    }
    var obj={
      a:2,
      foo:foo
    }
    function fun(fn){
       fn();
    }
    var a=3;
    fun(obj.foo);//3 本质上传入的是一个函数，和这个对象无关了

    ````
    - 显示绑定：在调用函数时指定这个this，入call，apply。
       
       + 隐绑定 本质上就是Function.prototype.bind()方法。
         ````js
           function bind(obj,fn){
              return function ()
              {
                return fn.apply(obj,arguments);
              }
           }
          function foo(){
           console.log(this.name);
          }
         var obj={
           name:"zsn"
         }
         function fn(fun){
          fun();
         }
          fn(bind(obj,foo));
          ````
          + API调用的“上下文”:第三方库，已经js语言和宿主环境中有许多内置对象，通常被称为“上下文“（context），作用和bind()一样。
             ````js
               function foo(item)
                 {
                   console.log(item,this.id);
                 }
              var obj={
                id:"sdfsd"
               }
              var arr=[1,2,3];
             arr.forEach(foo,obj);
             ````
     - new绑定：实际上并不存在”构造函数“，只有对于函数的”构造调用“；使用new来调用函数时，会构造一个新对象并把它绑定到函数调用的this上。
     - 判断this：new绑定》显示绑定》隐式绑定》默认绑定。
     - 被忽略的this，指向默认 两种用法：1）展开数组，2）参数柯里化（预先设置有些参数）；
       + 但是总是使用null来忽略this的绑定可能产生一些副作用。如果某个对象确实用到了this（如第三方库的一个函数），那默认绑定会把this绑定到全局对象。这将导致不可预计的后果（比如修改全局对象）。
       + 更安全的this，传入一个特殊对象：Object.create(null),就是一个空的非委托的对象。它和{}很像，但不会创建Object.prototype这个委托，所以它比{}更空。
      ````js 
      //展开数组 在es6中foo(...[2,3]);可以展开函数
         function foo(a,b){
           console.log("a:"+a+";b:"+b);
         }
         foo.apply(null,[2,3]);
         //柯里化
         var bar=foo.bind(null,"zsn");
         bar("clj");
      ````
      - 软绑定（完善了显示绑定（硬绑定）不能再修改绑定对象的问题）:对当前函数进行绑定一个默认的对象，并返回一个新函数。如果使用隐式绑定或则显示绑定，都可以修改这个新函数的绑定对象，但如果调用新函数时发现this指向是全局，则给这个新函数绑定默认对象。

 ````js
        if(!Function.prototype.softBind){
        Function.prototype.softBind=function(obj){
//            获取当前软绑定的函数
            var fn=this;
            //捕获所有curried参数
            var curried=[].slice.call(arguments,1);
            var bound=function(){
                return fn.apply(
//                        obj为默认的对象，如果this不存在或则为全局对象则用默认对象，否则则用本身的对象
                        (!this||this===(window||global))? obj:this,
                        //获取参数合并成一个整体
                        curried.concat.apply(curried,arguments)
                )
            };
            //创建一个当前软绑定对象原型对象的副本
            bound.prototype=Object.create(fn.prototype);
            return bound;
        }
    }

    function foo(){
        console.log("name:"+this.name);

    }
    var obj={name:"obj"},obj1={name:"obj1"},obj2={name:"obj2"};
    var fooOBj=foo.softBind(obj);
    fooOBj();//obj
    obj1.foo=foo.softBind(obj);
    obj1.foo(); //obj1

    fooOBj.call(obj2); //obj2
    setTimeout(obj1.foo,10);//obj
 ````
- this词法
  + 箭头函数是没有办法使用上述四条规则的。=>是根据外层的词法作用域（函数或全局）来决定this，具体的说，箭头函数会继承外层函数调用的this的绑定（无论this绑定的是什么）和es6之前的var self=this一样。箭头函数this无法修改（new也不行）
## 对象
## 内置对象 
+ typeof检查null为Object的原因：不同的对象再底层都表示为二进制，在javascript中二进制前三位为0的会被判断为object类型，null的二进制表示为0，自然前三位也是0，所以执行typeof时会返回”object“。

+ null和undefined只有字面量形式，而Date只有构造形式。
+ 对于Object，Array，Function，RegExp来说，无论使用构造形式，还是字面量形式，他们都是对象，不是字面量。
+ es6增加了可计算属性名
````js
 var str="dt"; 
 var obj={[str+"name"]:"zsn",[str+"age"]:"18"};
 ````
+ 复制对象
  - 对于JSON安全(可以被序列化为一个JSON字符串)的对象来说，有一种巧妙的方法：
   ````js
    var newObj=JSON.parse(JSON.stringify(someObj));
   ````
  - es6定义了一个浅复制方法：Object.assign()方法第一个参数是目标对象，之后可以是一个或多个源对象。并且返回目标对象。
 +属性描述符
   - Object.getOwnPropertyDescriptor(obj,"key") 返回一个对象，这个对象是对obj对象key属性的描述。writable(可写),enumerable(可枚举)和configurable(可配置)
   - Object.defineProperty 可对属性设置属性描述符
   - 只要属性是可配置的，就可以通过Object.defineProperty方法来修改描述符。把configurable修改为false为单向操作，无法撤销！除了无法修改外，还不能删除设置了configurable为false的属性。
   - 一个小小例外：设置了configurable为fasle，可以把writable状态由true改为false，但是无法把false改为true。
 ````js
 var myObject={a:2};
 Object.getOwnPropertyDescriptor(myObject,"a")
  //configurable:true
  //enumerable:true
  //value:2
  //writable:true

  var  myObject={}; 
  Object.defineProperty(myObject,"a",{ value:3,writable:false,configurable:true,enumerable:true})
 ```` 
+ 不变性
  - 禁止扩展 Object.preventExtensions();
  - 密封 Object.seal();在禁止扩展的基础上把现有属性标记configurable:false
  - 冻结 Object.freeze();在密封的基础上把现有描述符writable:false。
 ````js
 //禁止一个对象添加新的属性并且保留已有属性。
  var myObject={a:2};
  Object.preventExtensions(myObject);
  myObject.b="dsf";
  myObject.b //undefined
 ```` 
+ 对象默认的[[Put]]和[[Get]]操作分别对应着属性值的设置和获取。
````js
    var myObject={
        get a(){
            return this._a_;
        },
        set a(val){
            this._a_=val*2;
        }
    }
    myObject.a=2;
    console.log(myObject.a);
````    
+ 判断对象中是否存在属性的两个方式：
  - in操作符实际上是检查是否存在键，对数组尤其重要，判断的是索引、
  -  Object.keys()和Object.getOwnPropertyNames()都只会检查对象上是否存在属性，不查找原型链。
````js
var obj={name:"zsn"};
("name" in obj);//in会检查原型链 
obj.hasOwnProperty("name") //只会检查对象中是否有

4 in[2,3,4] //false

 obj.prototype.propertyIsEnumerable("name") //true 判断属性是否可枚举

 Object.keys()//返回可以被枚举的键数组
 Object.getOwnPropertyNames()//返回一个数组，包含所有属性
````
+ 遍历 
  - es6新增了遍历数组（如果对象本身定义了迭代器也可以遍历数组） for of 直接遍历值，不再只能遍历数组下标（或则对象属性）。
  - for..of循环首先会向被访问对象请求一个迭代器，然后通过调用迭代器对象的next()方法来遍历所有的返回值。
  - 数组有内置的@@iterator，因此for..of可以直接作用于数组。使用内置的@@iteratir来手动遍历数组。
  ````js
  var myArray=[1,2,3];
  var it=myArray[Symbol.iterator]();//ES6符号Symbol.iterator来获取对象的@@iterator内部属性
  //@@iterator并不是一个迭代器对象，而是可以返回一个迭代器对象的函数。
   it.next()  //{value: 1, done: false}
   it.next() // {value: 2, done: false}
   it.next()  //{value: 3, done: false}
   it.next() // {value: undefined, done: true}


   //可以给任何一个想遍历的对象手动定义一个迭代器
       var myObject={
        a:2,
        b:3
    }
    Object.defineProperty(myObject,Symbol.iterator,{
        enumerable:false,
        writable:false,
        configurable:true,
        value:function(){
            var o=this;
            var idx=0;
            var ks=Object.keys(o);
            return{
                next:function(){
                    return{
                        value:o[ks[idx++]],
                        done:(idx>ks.length)
                    }
                }
            }
        }
    })
  var it =myObject[Symbol.iterator]();
  console.log(it.next());
  console.log(it.next());
  console.log(it.next());

    for (var value of myObject) {

        console.log(value);
    }
    //使用了Object.defineProperty定义了自己的@@iterator,把符号位当作可计算属性名。  此外，也可以直接在定义对象时进行声明。
       var myObject={
        a:2,
        b:3,
        [Symbol.iterator]:function(){
            var o=this;
            var idx=0;
            var ks=Object.keys(o);
            return{
                next:function(){
                    return{
                        value:o[ks[idx++]],
                        done:(idx>ks.length)
                    }
                }
            }
        }
    }

  ````
 ## 混合对象类 
 ## 原型
 - 对原型上的属性设置了禁止修改的属性描述，那么和它关联的对象的该属性也不能修改，需要的时候只能从原型上获取，不能覆盖原型上的这个属性。
 - （原型）继承
   + 使用Object.create()可以创建一个新的对象，这个对象会指向原型（参数）对象（新对象有一个_proto_属性），和组合寄生继承原型的方式类似。但本身没有constructor。
   + es6增加了一个可以关联原型的方法：Object.setPrototypeOf(Bar.prototype,Foo.prototype);可以直接给子类原型加一个_proto_关联。
   + Object.create()方法会带来轻微性能损失（抛弃的对象需要垃圾回收），但它比es6及其之后的方法更短而且可读性更高。

 ````js
    function Foo(name){
        this.name=name;
    }
    Foo.prototype.myName=function(){
        return this.name;
    }
    function Bar(name,label){
        Foo.call(this,name);
        this.label=label;
    }
    //丢失了Bar.prototype.constructor
    Bar.prototype=Object.create(Foo.prototype);
    Bar.prototype.myLabel=function(){
        return this.label;
    }
    var a=new Bar("a","obj a");
    console.log(a.myName());
    console.log(a.myLabel());
    console.dir(Bar.prototype);
```` 
 - 检查“类”的关系
    + instanceof操作符左边是一对象，右边是一函数。instanceof检查的是，左侧对象的原型链中有没有一个canstructor指向右侧函数的。
    + Object.prototype.isPrototypeOf()判断在当前对象的原型链中是否有构造函数的原型。
    + Object.getPrototypeOf()可以获取一个对象构造函数的原型
 - 创建关联
    + Object.create就是让tt有一个_proto_指向了aa（把aa当成了原型对象）。使两个对象关联起来了
    +  Object.create是在es5新增的函数，es5之前的环境如果需要支持这个功能要使用一段简单的polyfill代码，它部分实现了Object.create功能
    ````js
      var aa={age:18};
      var tt=Object.create(aa);
      //polyfill
      if(!Object.create){
          Object.create=function(o){
              function F(){};
              F.prototype=o;
              return F();
          }
      }
    ````      
  - 小结
     + 虽然这些JavaScript机制和传统面向类语言中的“类初始化”和“类继承”很相似，但JavaScript中的机制有一个核心区别，那就是不会进行复制，对象之间是通过[[Prototype]]链关联的。相比继承，委托是一个更合适的术语，因为对象之间的关系不是复制而是委托。

## 行为委托
  - JavaScript中原型链这个机制本质上就是对象之间的关联关系     
  - 委托理论
      + 相对于面向对象，这种编码风格称为“对象关联”，委托行为意味着某些对象在找不到属性或则方法引用时会把这个请求委托给另一个对象。这是一种极其强大的设计模式，对象并不是按照父类到子类的关系垂直组织的，而是通过任意方向到的委托关联并排组织的。

````js
   var Task={
        setID:function(ID){this.id=ID;},
        outputID:function(){console.log(this.id)}
    }
    var xyz=Object.create(Task);
    xyz.prepareTask=function(ID,Lable){
        this.setID(ID);
        this.lable=Lable;
    }
    xyz.outputTaskDetails=function(){
        this.outputID();
        console.log(this.lable);
    }
    xyz.prepareTask("18","clj");
    xyz.outputTaskDetails();
````
    <file_sep>//如果需要载入自己写的文件，路径必须采用点开头的路径
const cal= require('./calculator')

//模块内部定义的成员只能在模块内部使用，
//除非挂载到全局对象上（global）。最好不要这样做，因为会污染全局作用域。
//module 就是模块上下文 exports
console.log(cal.add(2,3));<file_sep><title>vue</title>

## vue基本语法和webpack初级(8.4)

1. vue是一个渐进式框架，关注视图层
2. 组件 高度的封装
3. 虚拟DOM树，Virtual Dom就是在js中模拟DOM对象树来优化DOM操作的一种技术或思路

#### webstrom配置

1. 推荐使用webstrom2016
2. vue智能提示配置
   - File>Settings>Plugins=>vue.js(Browse repositories...)
   - File>Settings>Languages&Frameworks>ECMAScript 6
   - Editor>File and Code Templates（在File Types查看是否配置成功）>+
   ![](./img/snipaste_20170804_191058.png)

### vue基本语法

1. vue基本使用
```js
 
     <!--引入vue.js文件-->
    <script src=vue.min.js></script>
    <!--我们在html页面留了个坑-->
    <div id="app">
        {{ msg }}
    </div>
    <script>
//        创建vue的一个实例化对象
        var vm = new Vue({
            el:'#app',
            data:{
                msg:'欢迎来到vue课程'
            }
        })
    </script>
```
2. v-model和v-text，v-html
```js
        <!--v-model是双向绑定的-->
        <!--v-model是vue中唯一一个双向绑定的,其他的都不是-->
        <input v-model="title" type="text">

        <!--v-text一般用于除了input以外的其他标签-->
        <span v-text="msg"></span>
        
        <!--v-html一般用于解析html-->
        <div id="app">
        <span v-html="msg"></span>
        </div>
        <script>
            var vm = new Vue({
                el: '#app',
                data: {
                msg: '<h1>天气不错</h1>'
              }
            })
        </script>
```
3. v-bind对标签属性值进行绑定，两种写法
```js
   <div id="app">
        <a v-bind:title="tip"  v-bind:sdjfoisodifwfe="tip">这是个a标签</a>

        <!--v-bind的简写形式-->
        <a :title="tip">这是个a标签</a>
    </div>
    <script>
        var vm = new Vue({
            el:'#app',
            data:{
                title:'可以出去玩',
                tip:'这是个title'
            }
        })
    </script>
```
4. v-on绑定事件，两种写法
```js
     <div id="app">
        <!--v-on一般是进行事件处理的-->
        <button v-on:click="btnClicked">按钮</button>

        <!--v-on有一个简写形式-->
        <button @click="btnClicked"></button>
    </div>
    <script>
//        这样写可以接收到事件,但是不推荐这样写
//        function btnClicked() {
//            alert(1)
//        }
        var vm = new Vue({
            el:'#app',
            data:{
                title:'我很高兴来到这里'
            },
            methods:{
                btnClicked(){
                    alert(2)
                }
            }
        })
    </script>
```
5. v-if隐藏后连代码都没有了，只留下一个注释占位符;v-show 隐藏后代码还在，只是设置display:none
```js
    <div id="app">
        <span v-if="isTrue" v-text="msg"></span>
         <!-- <span v-show="isTrue" v-text="msg"></span> -->
        <button v-on:click="click">按钮</button>
    </div>
<script>
    var vm = new Vue({
        el:'#app',
        data:{
            msg:'天气不错',
            isTrue:true
        },
        methods:{
            click(){
                this.isTrue = !this.isTrue
            }
        }
    })
</script>
```
6. v-for三种用法
```js
    <div id="app">
        <!--v-for的第一种用法-->
        <ul>
            <li v-for="item in names">
                {{item}}
            </li>
        </ul>
        <!--v-for的第二种用法-->
        <ul>
            <li v-for="(item,index) in names">
                {{item}}------{{index}}
            </li>
        </ul>
        <!--v-for的第三种用法-->
        <ul>
            <li v-for="(value,key,index) in user">
               {{value}}---{{key}}-=---{{index}}
            </li>
        </ul>
    </div>
    <script>
        var vm = new Vue({
            el:'#app',
            data:{
                names:['狼厂','鹅厂','鸟厂','马厂','数字工厂'],
                user:{name:'老梅',age:'17.5'}
            }
        })
    </script>
```
7. 私有组件和全局组件
  - 写在Vue对象中为components私有组件时，组件名称不能大写，名称中可以有-。
  - 组件标签在使用时内部不能带有内容，内容会被定义组件时模块中的内容替换。
  - 注意：template属性必须给根元素,创建全局组件必须在Vue对象实例化前进行
```js
   //私有组件
   <div id="app">
        <privatecomponent></privatecomponent>
    </div>
    <script>
        var vm = new Vue({
            el:'#app',
            components:{
                'privatecomponent':{
                    template:'<h1>这是第一个私有组件</h1>'
                }
            }
        })
    </script>

     //全局组件的定义
     <div id="app">
        <my-component/>
    </div>
    <div id="app1">
        <my-component></my-component>
    </div>
    <script>
        Vue.component('my-component',{
            template:'<my-haha</my-haha>'
        })

        Vue.component('my-haha',{
            template:'<h1>my-haha的使用</h1>'
        })
        var vm = new Vue({
            el:'#app',
        })
        var vm1 = new Vue({
            el:'#app1'
        })
    </script>
```
8. 私有过滤器和全局过滤器
  - 管道前面的输出等于后面的输入，管道符前面的变量输出成为管道符后面函数的输入
```js
//私有过滤器
    <div id="app">
        {{ msg | toLower }}
    </div>
    <script>
        var vm = new Vue({
            el:'#app',
            data:{
                msg:'MSDJFSOIFDWOEIF'
            },
            filters:{
                // 实现一个小写转化的过滤器
                toLower(input){
                    return input.toLowerCase()
                }
            }
        })
    </script>

    //全局过滤器
     <div id="app">
        {{title | toUpper}}
    </div>
    <script>
        Vue.filter('toUpper',function (input) {
            return input.toUpperCase()
        })

        var vm = new Vue({
            el:'#app',
            data:{
                title:'sdojfiodifosdfs'
            }
        })
    </script>
```

#### router模块和传值：vue-router.min.js

```js

 <script src="vue.min.js"></script>
 <script src="vue-router.min.js"></script>
 <div id="app">
    <!--路由传值的方式-->
    <a href="#/share/2">go</a>
    <!--router-link是对a标签的封装，推荐使用-->
   <router-link to="/share/1">go</router-link>
    <!--router-view路由内容专用填坑-->
    <router-view></router-view>

 </div>
 <script>

    //全局组件
  var share= Vue.component('share',{
      data(){   //添加全局临时变量
        return {
            number:''
        }
      },
      //这个方法是在当前组件全部加载完成以后调用的方法
      created(){      //调用外面传递的值
          this.number=this.$route.params.id
        },
      props:['id'],  //接收外面传递的值
      template:'<h1>我的天啊{{number}}</h1>' //template必须给根元素
  });
  var rt=new VueRouter({
      routes:[         //存放路由信息的数组
          {name:'share',path:'/share/:id',component:share}
      ]
  });
  var vm =new Vue({
        el:'#app',
        router:rt,   //关联路由
        data:{
            title:'静态网站'
        }
    })
 </script>

```
#### $http异步模块：vue-resource.min.js

```js
 //get
 <script src=vue.min.js></script>
 <script src=vue-resource.min.js></script>
 <body>
    <div id="app">
        {{title}}
        <br>
        <button v-on:click="btnBeClicked">发送get请求</button>
        <br>
        {{message}}
    </div>
    <script>
        var vm = new Vue({
            el:'#app',
            data:{
                title:'get方法的介绍',
                message:[]
            },
            methods:{
                btnBeClicked(){
                    // 发送请求
                    var url = 'http://192.168.3.11:8899/api/getlunbo'
                    this.$http.get(url).then(
                            function (data) {
                                this.message = data.body.message
                            },
                            function (err) {

                            }
                    )
                }
            }
        })
    </script>

    // post 
          
        var url = 'http://192.168.3.11:8899/api/getlunbo'
        this.$http.post(url,{content:'xxxxxxxx'},{emulateJSON:true}).then(
                function (response) {

                    },
                    function (err) {
                            console.log(err)
                        }
                    )
    // JSONP
         this.$http.jsonp(url).then(
                        function (response) {

                        },
                        function (err) {
                            console.log(err)
                        }
                )                

```

### webpack

1. 插件  Plugins
2. 出口和入口的设置
3. 加载器 Loaders
4. 安装(先npm init初始化环境)
   - 第一步 npm i webpack -g （只要安装一次）
   + 第二步 npm install webpack --save-dev （创建一个新项目就安装一次）
      - -dev 开发依赖，打包就不会带这个包
   + 第三步
      - 在实际开发环境中，存在多个配置，发布和开发以及其它运行不同的配置
      - webpack.develop.config.js //开发时候的配置
      - webpack.publish.config.js //发布时候的配置
      - webpack --config webpack.develop.config.js //运行打包 
      - webpack.config.js 只有一个配置 cmd中执行webpack就可以了  
   + 第四部
     - 加载器：去git上查找下载安装babel-loader的方法
     - 看README.md或者git中查看配置方法。
   + 第五步配置config(webpack.develop.config.js)

```js

 // webpack开发时候的配置文件
  const path=require('path')

  module.exports={
    //webpack中的三大亮点之一：入口出口设置
    entry:path.resolve(__dirname,'./src/app.js'),//入口
    //出口设置，把项目打包的位置
    output:{
        path:path.resolve(__dirname,'dist'), //导出到指定的文件夹
        filename:'tt.js'         //导出的文件名
    },
     //webpack中的第二个亮点：加载器loader
     module:{
        rules:[
             // babel-loader的加载器信息
            {
                test: /\.js$/,
                exclude: /(node_modules|bower_components)/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['env']
                    }
                }
            }
        ]
    }
  }

```   
     
#### 知识点
1. vetur给vscode使用的vue语法提示高亮的插件
2. Moment.js 日期过滤器
3. npm run 名称 --可以直接使用package.json中scripts配置的快捷命令
4. nrm test 测试镜像反应速度
5. 安装webpack的文件夹名字不能叫webpack，程序会认为在webpack中安装webpack会报错


## 加载器和插件(8.5)

### vue基本的三个组成部分：组件模板，项目入口，承载单页面
1. 引入vue模块前要先下载
  - npm i vue --save

```js
    // 一个拥有独立html，css，js的组件模板app.vue
    <template>
     <section>
       <h1>苍茫的天涯是你的哎</h1>
       <img src="./images/img1.gif" alt="">
     </section>
    </template>
    <script></script>
    <style>
      h1{
          background-color: hotpink;
       }
    </style>
```
```js
    //项目入口main.js文件
    import Vue from 'vue'         //导入vue.js
    import app from './app.vue'  //导入vue模板
    const vm=new Vue({
    el:'#app',
    //必须使用箭头函数
    render:create=>create(app)     //渲染到index.html留的坑里
  });
  
```

```html
<!--进行展示的index.html页面-->
<div id="app">
    <app></app>
</div>
<script src="./tt.js"></script>

```

### 加载器（本质上就是通过一些优化把开发代码转化为发布的代码）

1. vue-loader  1.0配置中可以不写-loader，2.0必须写
2. 加载器在使用前要先进行下载
3. 三种加载器：ES6加载器，css加载器，图片加载器
4. 先执行加载器，然后导入src中的文件，生成后在导出
5. 加载器的两种配置方式，参考图片加载器

```js
   const path=require('path');
   const webpack=require('webpack');
   module.exports={
       //webpack中的三大亮点之一：入口出口设置
       entry:path.resolve(__dirname,'./src/main.js'),
       //出口设置，把项目打包的位置
       output:{
           path:path.resolve(__dirname,'dist'), //导出到指定的文件夹
           filename:'tt.js'         //导出的文件名
       },
       //webpack中的第二个亮点：加载器loader
       // 加载器解析.vue等格式的文件
       module:{
          rules:[
                 {  //把ES6解析到ES5的加载器 jsx（使代码混编，一个文件中写css，html，js）转化js，
                     test: /\.js$/,
                     exclude: /(node_modules|bower_components)/,
                     use: {
                         loader: 'babel-loader',
                         options: {
                             presets: ['env']
                         }
                     }
                 }, {  //解析vue的加载器
                     test: /\.vue$/,
                     use: {
                         loader: 'vue-loader'
                     }
                 }, {  //解析css的加载器
                     test: /\.css$/,
                     use: [ 'style-loader', 'css-loader' ]
                 }//解析图片加载器 url-loader
                 ,
                 // {
                 //     test: /\.(png|jpg|gif)$/,
                 //     use: [
                 //         {
                 //             loader: 'url-loader',
                 //             options: {
                 //                 limit: 8192
                 //             }
                 //         }
                 //     ]
                 // }
                 {
                     test: /\.(png|jpg|gif)$/,
                     use:'url-loader?limit=25000&name=images/[name].[ext]' 
                     //1byte=8bit(位) bit 小于这个限制则     
                     //把图片转为base64字符串，这个时候可以base64字符串通过css设置背景图片减少图片http请求
                 }

        ]
    }
   
}
```

### 插件(webpack第三个亮点：插件的使用)

插件分为两种：webpack自带的不需要安装，第三方的需要安装

#### webpack-dev-server（修改项目，打包文件自动更新服务）配置三部曲

本质上通过webpack内置的热更新插件实现

```js
        //1.下载 npm install webpack-dev-server --save-dev
      
       //2.webpack.develop.config.js 配置
       const webpack=require('webpack');
       module.exports={
               //...省略若干配置
              devtool: 'eval',
              devServer: {
                 contentBase: path.resolve(__dirname, './src'), // 当前服务器监听的路径
                 hot: true,  // 热更新
                 port:8080,  // 定义端口号
                 host: 'localhost',
                 open:true    // 是否自动打开浏览器
                 openPage:""   //配置open:true时，必须给一个openPage:""默认配置
              },
             plugins: [
             //  html-webpack-plugin
              new webpack.HotModuleReplacementPlugin(), //热更新插件
             ]
      
        }
        // 3.package.json配置  
        //"server":"webpack-dev-server --config webpack.develop.config.js --content-base src"
      
        //npm run server
```

#### 压缩插件（一般不用，效果不好），分离第三方包的插件，自动生成html的插件

1. 压缩文件一般使用在命令中加-p,压缩比内置压缩插件效果更好（更小）
   - "pb": "webpack --config webpack.publish.config.js -p",
2. 自动生成html的插件是第三方插件
   - html-webpack-plugin

```js
 const HtmlWebpackPlugin = require('html-webpack-plugin');
 plugins:[
        // 压缩插件(一般不用)
        // new webpack.optimize.UglifyJsPlugin({
        //     beautify: false,
        //     mangle: {
        //         screw_ie8: true,
        //         keep_fnames: true
        //     },
        //     compress: {
        //         warnings: false,
        //         screw_ie8: true
        //     },
        //     comments: false
        // }),

        // 分离第三方的插件，不是我们自己写的模块
        // 抽取第三方的插件（如vue.js）                             所有第三方文件打包的文件
        new webpack.optimize.CommonsChunkPlugin({name:'vendors',filename:'vendors.js'}),

        // 自动生成html的插件
        // new HtmlWebpackPlugin({
        //     title: 'My App',
        //     filename: 'index.html',
        //     template: 'src/index.html',
        //
        // })
        new HtmlWebpackPlugin({
            template: './src/index.html',
            htmlWebpackPlugin: {
                "files": {
                    "js": ["bundle.js","vendors.js"] //设置引用的文件，自动生成标签插入html中
                }
            },
            // 进行压缩,情怀至上
            minify: {
                removeComments: true,
                collapseWhitespace: true,
                removeAttributeQuotes: true
            }
        }),
    ]
```


### ESLint配置（编码规范）

1. npm install  eslint -g （可能需要配置本地 npm install  eslint --save-dev）
2. npm install babel-eslint -g（可能需要配置本地 npm install babel-eslint --save-dev）
3. package.json的scripts中配置快捷命令 
  - "lint": "eslint --ext .js .vue src"
4. 添加自定义代码规范，在全局根目录下添加文件.eslintrc.js
  - 运行后eslint --init，根目录中将有一个.eslintrc文件
5. 在webpack.develop.config.js开发配置文件中配置  

```js
  //.eslintrc.js
  // eslint的语法规则

    module.exports = {
        // 开启推荐配置信息
        // "extends": "eslint:recommended",
        // 默认情况下，ESLint 会在所有父级目录里寻找配置文件，一直到根目录。如果你想要你所有项目都遵循一个特定的约定时，这将会很有用，但有时候会导致意想不到的结果。为了将 ESLint 限制到一个特定的项目，在你项目根目录下的  package.json 文件或者 .eslintrc.* 文件里的 eslintConfig 字段下设置 "root": true。ESLint 一旦发现配置文件中有 "root": true，它就会停止在父级目录中寻找。
        "root": true,
        // 脚本在执行期间访问的额外的全局变量
        // 当访问未定义的变量时，no-undef 规则将发出警告。如果你想在一个文件里使用全局变量，推荐你定义这些全局变量，这样 ESLint 就不会发出警告了。你可以使用注释或在配置文件中定义全局变量。
        "globals" : {
            "window":true,
            "document":true,
            "$":true
        },
        // 设置插件
        // "plugins": [
        //     'html'
        // ],
        // 设置解析器选项（必须设置这个属性）
        "parserOptions": {
            "ecmaVersion": 7,
            "sourceType": "module",
            "ecmaFeatures": {
                "jsx": true,
                // "arrowFunctions": true,
                // "experimentalObjectRestSpread": true,
                // "classes": true,
                // "modules": true,
                // "defaultParams": true
            }
        },
        // 启用的规则及各自的错误级别
        "rules" : {
            // 禁止用console
            "no-console":2,
            // 禁止用分号
            "semi":[1,'never'],
            // 在同一个作用域中禁止多次重复定义
            //"no-redeclare":1
        },
        // 指定你想启用的环境
        "env": {
            "browser": true,
            "node": true
        },
        "parser": "babel-eslint"
    };
```
```js
 //配置文件（config）配置信息
  module.exports={
    //。。。。其它各种配置
    module:{
        rules:[
            // eslint-loader的配置信息
            // exlint-loader的加载器配置信息
            // 配置编码规范的时候需要ES6解析到ES5的加载器配置到其中
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: [
                    'babel-loader',
                    'eslint-loader'
                ],
                //webpack配置改了，要修改配置方法
                // query: {
                   // cacheDirectory: true
               // } 
            },
            { //只是检查的时候用，调试时候注释
                test: /\.vue$/,
                enforce: 'pre',  
                include: /src/, 
                use: [{
                    loader: 'eslint-loader',
                    options: {
                        formatter: require('eslint-friendly-formatter')   // 
                    }
                }]
            },
            // {  //把ES6解析到ES5的加载器
            //     test: /\.js$/,
            //     exclude: /(node_modules|bower_components)/,
            //     use: {
            //         loader: 'babel-loader',
            //         options: {
            //             presets: ['env']
            //         }
            //     }
            // },


              
              //其它配置，各种加载器

        ]
    },
  }
```

- MUI-最接近原生APP体验的高性能前端框架

## mui和mint-ui（8.7）

- mui和mint-ui先git把两个项目从githab上克隆下来（根据线上的demo到两个克隆的包里找代码）
- 然后npm把两个包下载到项目中。

#### vue模板

```js
    <template>
    
    </template>
    <!--scoped：保证样式只在组件内部使用-->
    <style scoped>
    
    </style>
    <!--让编辑器识别ES6，不报错-->
    <script type="text/ecmascript-6">
       
    </script>
```
```js
  //一个页面级组件script标签中内容
  import banner from './Tools/banner.vue'
  import url from './Tools/Url.js'

    export default{
       data(){
           return {
               banners:[]
           }
       },
        created(){
            this.getBanner()
        },
        methods:{

           getBanner(){
               this.$http.get(`${url.HTTP}${url.SERVER_PATH}:${url.PORT}/api/getlunbo`).then(rel=>{
                  console.log(rel)
                   this.banners=rel.body.message
               },rej=>{})
           }
        },
        components: {
            banner
        }


    }
```
```js
       //url-loader配置图片和字体解析
       { test: /\.(gif|jpg|png|woff|svg|eot|ttf)$/,
        use: 'url-loader?limit=50000&name=[path][name].[ext]'}
```
```js
  //webpack入口js引入模块方式
  import  vueResource from 'vue-resource'
  Vue.use(vueResource)
```

- linkActiveClass:'mui-active'路由模块的该属性可以配置选中a标签的类名。
- 打包代码执行顺序：先执行加载器，把内容导入到webpack,执行插件然后导出
- mui代理官网
  > http://www.dcloud.io/mui.html 
- badge 徽标，类似于购物车上的红色小圆（显示数字）
- 图片设置环绕，图片过大（不用缩小图片）：background-repeat: round;
- Mockplus原型设计工具


## 组件传值，路由传值(8.8)

- 组件传值，路由传值
```js
    //组件调用传值
   <banner :bannerdt="banners"></banner>

   //组件内部获取值
   <script type="text/ecmascript-6">
       export default{
           //如果是路由传值要使用这种方式获取值使用,如果是组件传值，直接在模板里使用
            methods:{
                 get(){
                     this.$route.params.id
                 }
            }
            //组件传值，路由传值都要通过这种方式接收
           props:['bannerdt']
       }
  </script>
```
- 动态生成router-link标签的两种方法
```html
   <router-link :to="{name:'newsinfo',params:{id:item.id}}">
    <router-link v-bind='{to:"/newsDetail/"+item.id}'>
```
- 好用的日期过滤器：momentjs
- IM即时通讯(网易云)
- face++人脸识别服务
- ping++可以简便使用多种支付方式的开发者平台
- white-space: nowrap;文字不换行


## 图片详情页，商品列表页（8.10）

- ref:获取修改表单原生的值
```js
 <textarea placeholder="请输入评论内容"></textarea>
 //this.$refs.textArea1.value 
```
- 假请求：当配合安卓或ios开发的时候，需要调用底层（如拍照），这个时候可以发送一个请求，让安卓的同事去实现功能。

- vue-picture-preview 图片预览组件，组件标签要放在图片的上方。

```js
 export default{
        data(){
            return{
                isBack:true
            }
        },

        methods:{
            back(){
                 //路由对象，回撤
                this.$router.go(-1);
            }
        },
        //监控一个对象
        watch:{
            //url对象
            '$route':function (newValue,oldValue) {
                if(this.$route.path==="/home"){
                    this.isBack=false;
                }else {
                    this.isBack=true;
                }
            }
        }，
        //注册页面所要用到的组件
         components:{
            banner
        },
    }
```
+ style的scoped独立样式作用域
 - 添加scoped之后，实际上vue在背后做的工作是将当前组件的节点添加一个像data-v-1233这样唯一属性的标识。
 - 当然也会给当前style的所有样式添加[data-v-1233]这样的话，就可以使得当前样式只作用于当前组件的节点。
 - 所以如果在父组件中修改子组件的样式不能加scoped属性。


## 动画实现，组件向父组件传值（8.11）

### 传值的两种方式

- 父子组件之间的传值
```js
     //子组件
    export default{
        data(){
            return {
                number:1
            }
        },
        methods:{
            add(){
                this.number++
                document.querySelector("#numb").innerText=this.number
                this.submitCount()
            },
            jian(){
                if(this.number>1){
                    this.number--
                    document.querySelector("#numb").innerText=this.number
                    this.submitCount()
                }
            },
            submitCount(){
                this.$emit('count',this.number)//count：父组件接收值的标记
            }
        }
    }

    //父组件

    //父组件调用是监控count标记把值传入getCount方法中。
    <number v-on:count="getCount"></number>

    methods:{
        //通过方法的形式获取子组件传递的值
        getCount(num){
                this.count=num
            }
    }
```

- 没有直接关联的组件传值

```js
   //在一个js文件中定义一个独立的vue对象保存值（中转站）
   import Vue from 'vue'
   export var mddata=new Vue()  //导出一个vue

   //传值的组件
   //使用这个值的地方进行引用
   import {mddata} from './Tools/middledata'
   //传递值组件调用的方法
    mddata.$emit('shopcar',this.count)

   //获取值的组件，值传递的约定标识shopcar
   import {mddata}from './components/Tools/middledata'
   mddata.$on('shopcar',function (number) {
      
    })
```

### 动画实现

- 贝塞尔曲线：可以设置动画运动轨迹速度的快慢
- js钩子hook》中间件，动画经过的事件就是一系列的中间件
```js
   //对要进行运动的元素设置css
   transition: all 1s cubic-bezier(.56,-0.42,.36,.59);

  //设置元素在运动时经过的方法
                <transition
                        v-on:before-enter="beforeEnter"
                        v-on:enter="enter"
                        v-on:after-enter="afterEnter"
                >
                <div v-show="isshow" class="bill"></div>
                 </transition>

    //  在methods中设置动画要调用的方法      
           // 进入之前调用的一个方法
            beforeEnter: function (el) {
                el.style.transform = "translate3d(0,0,0)"
            },
            // 进入时候调用的方法
            enter: function (el, done) {
                var offset = el.offsetWidth  //设置这个使元素感觉到属性在发生变化
                el.style.transform = "translate3d(60px,303px,0)"
                done()
            },
            // 进入完成以后调用的方法
            afterEnter: function (el) {
                this.isshow = !this.isshow
            },      
```





## vue生命周期，indexdb数据库，两种手机端app打包方式（8.13）


1. 关于Vue实例的生命周期

![](./img/3504099265-580628fd03258_articlex.png)
![](./img/3346068135-580822cd52898_articlex.png)

2. 两种手机端app打包方式
- dcloud：源码会泄露给第三方
   + HTML5+ 》 HBuilder  
- 本地环境：布置环境麻烦
   + 生成手机app的环境配置： java jdk和安卓（2.2.2.0）sdk及其环境变量；全局安装ionic(开发工具)  cordova（打包工具）包
     - java jdk安装成功：cmd运行java -version和javac成功
     - 安卓（2.2.2.0）sdk：可以安装配置不够，还要通过SDK Manager.exe继续安装
     - npm i -g ionic cordova
     - 生成一个app项目：ionic start ioicApp（项目名称）
     - cordova run android 打安装包

![](./img/snipaste_20170813_165955.png)     
![](./img/snipaste_20170813_093921.png) 

![](./imgsnipaste_20170813_103728.png)  
![](./img/snipaste_20170813_102712.png)  


#### can i use：可以查看属性兼容性

<file_sep>[TOC]

## 流行框架 Angular第一天

### 1.为什么要学习Angular?
- 使我们做单页面应用更加容易
- Angular自身有很多颠覆性的特性 改变了前端的编码方式 简化了我们的操作
- 火,就业需要

### 2.Angular是什么?
- 一款非常优秀的前端高级JS框架
- 由谷歌团队负责开发维护


### 4.框架与库

- 无论是angular还是jQuery都是用原生JS封装的
- 库: 
    + 对代码进行封装 调用封装的方法 简化操作
        * jquery 针对DOM操作
        * requirejs js模块化
- 框架：
    + 虽然也是调用封装的方法
    + 但是更多的框架会提供代码书写的规则
    + 我们要按照规则去写代码 框架会帮我们实现相应的功能

### 5.Angular核心思想
- write less do more
- 其核心是通过指令扩展了HTML，通过表达式绑定数据到HTML。
- Angular不推崇DOM操作，也就是说在NG中几乎找不到任何的DOM操作
- 一切操作以数据为中心，用数据驱动DOM

### 6.获取angular的方式
+ [在官网上下载](http://angularjs.org)
+ [通过CDN的方式引入到页面中](https://cdn.bootcss.com/angular.js/1.6.4/angular.min.js)
+ `<script src="https://cdn.bootcss.com/angular.js/1.6.4/angular.min.js"></script>`

### 7.Angular快速入门 (hello world)

- 基本的指令及表达式
    + 在使用了angular的页面，以ng-开头的属性，都可以称之为指令
    + ng-app
        * 告诉angular它在页面中所要控制的范围
        * 页面加载完成angular会自动在页面中查找这个指令
        * 如果页面中没有这个指令,angular将不会启动
    + ng-model
        * 实现双向数据绑定
    + ng-click
        * 点击事件(和原生JS中的onclick事件作用一样)
    + ng-init
        * 初始化数据
    + 表达式介绍
        * {{}} 这种双大括号的形式称之为插值表达式
        * 在表达式中可以写ng中的变量
        * 可以显示字符串
        * 在表达式中可以进行计算
        * 可以在表达式中写三目运算符
        * 执行angular函数
- 画图分析Angular实现双向数据绑定的原理

### 8.Angular模块化开发
- 模块化开发带来的好处
    + 方便管理, 复用,后期维护方便
    + 解决代码冲突,方便多人协作开发
- 分析模块和控制器与页面之间的关系
- 定义模块的语法规则
- 定义模块时第二个参数加与不加的区别
    + 加第二个参数是创建模块
    + 不加第二个参数是获取模块
- 报错信息分析
    +  Module 'myApp' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
    + 'myApp' 这个模块是不可获取的！ 你要么拼写错误 要么忘了加在他 你要确保一个有效的模块 做为了第二个参数的依赖

- 模块依赖
    + 主模块依赖了其他模块 就相当于拥有了其他模块的功能

### 9.Angular中如何使用控制器
- 定义及使用控制器
- 控制器的作用
    + 向页面中赋初始值
    + 向页面中展示数据
    + 代码分类

### 10.单页面应用程序介绍 (simple page application)
- 回顾传统网站(http://www.kuoda.com.cn/indexs.html)(图)
- 单页应用网站(http://m.daqizhong.com/index.html#/)
- 解释单页面应用(图)
- 单页面应用实现原理(图)
- 单页面应用程序的特点
    + 整个网站由一个页面构成
    + 公共部分只加载一次
    + *利用Ajax局部刷新达到页面切换的目的*
    + 不会发生页面跳转白屏的现象
    + *锚点与页面对应*

- 原生JS实现一个简单的单页面demo
- angular实现一个简单的单页面demo

### 11.Angular中的路由
- 通过画图分析在生活中路由起到什么样的作用
- Angular中的路由使用方法
    + [官方说明文档](https://docs.angularjs.org/api/ngRoute)
    + 如何获取路由模块
        * [通过CDN的方式引入到页面中](https://cdn.bootcss.com/angular.js/1.6.4/angular-route.min.js)
    + 在html页面中通过script标签的方式引入路由模块文件
    + 在应用主模块中依赖路由模块ngRoute
    + 配置路由
        * 通过config方法注入$routeProvider
        * 介绍配置中常用的参数
    + 路由和控制器的整合
    + 使用templateUrl方式载入模板需要在http环境下(本地file协议下不支持)

### Angular相关网站
- [官方网站](http://angularjs.org)
- [官方文档](https://code.angularjs.org/1.6.4/docs/api)
- [学习网站]
    + http://www.angularjs.net.cn
    + http://www.apjs.net
    + http://www.runoob.com/angularjs/angularjs-tutorial.html<file_sep>function $require(x){
    //1.根据文件路径找到js文件
    const fs=require('fs');
    const path=x
    //2.读取文件内容
    const content=fs.readFileSync(path,'utf8')
    //3.执行代码
    const $module={
        $exports:{}
    }
    const $exports=$module.$exports
    const code=`(function($exports,$module,$require){
         ${content}
    }($exports,$module,$require))
     
    `
   eval(code);
    //4.想办法拿到module.exports
    //4.return
    return $module.$exports;
}
var cal= $require('./calculator.js');
console.log(cal.add(2,3))

//inspect 调试n
//谷歌插件 pejkokffkapolfffcgbmdmhdelanoaih

//unsplash.it/300/200?random https://unsplash.it/800/800?random
//传入一个模块名称
const fs=require('fs');
//传入一个相对路径
const module1=require('./calculator')
//传入绝对路径（基本不会用）
//没有可移植性
const module2=require('C:/czbkqd/就业班/笔记/node/lib/require.js')
//1.start with .:./ ../
//都是按照相对路径方式找到文件
//相对路径得考虑基准路径
//2. start with [a-z0-9]:fs path http
//都是去找预制模块（系统内置，第三方），传入的是模块的名称

//3. start with  /or c:/ :
//绝对路径（没有争议）


//扩展名可省略得情况  js，json,node(原生扩展文件)
//require 可以直接载入json文件
//如果文件名是index，可以默认不写，如果有和目录同名得文件，则找这个文件


//node原生API不够满足需求时，可以c++ 写


//require.main 可以用来获取入口模块
//可以手动删除缓存

// 13241087977
// <EMAIL>
// 27102514

//把所有得require全部置顶
const path=require('path')
// path不会判断路径存在与否，只是单纯得字符串操作。
path.join()//将多个路径合并到一个路径中，并格式化

path.join(__dirname,'')//把相对路径转化为绝对路径

//join只是单纯得拼接
//resolve会在前一个路径作为基准路径，然后cd操作 后面绝对路径会覆盖第一个
//参数可能绝对路径得时候用
path.resolve()
//normalize.css 对样式进行初始化。


// iconv-lite npm install<file_sep>- 由于IE8及更早版本将NodeList实现为一个COM对象，而我们不能像使用JScript对象那样使用这种对象使用数组的方法（[].slice.call(nodes,0)）把它转化为数组会导致错误，必须手动枚举所有成员。
