原生JavaScript学习历程笔记

编译性语言:c c++有中间文件生成,最后直接执行中间文件

解析性语言:javascript  php无中间文件生成,翻译一句执行一句

java是通过jvm翻译


同步:计算机里面同步指的是非同一时间进行的事件

异步:计算机里面的异步指的是同时进行的事件

多线程:一个执行体同一时间可以做多件事

单线程:一个执行体同一时间只能做一件事


JavaScript是解释性语言,单线程 

js执行线程:轮转时间片,类似于吃饭,一共3碗菜1碗饭,一口一口吃最后都吃完了,分成一点一点随机执行


js三部分:ECMAScript、DOM、BOM

主流浏览器(有自己的内核):

IE              trident

Chrome          webkit/blink

firefox         Gecko

Opera7之前      presto

Safari           webkit

    <!-- js引入方法一:页面级js代码,可以写在任意位置
    <script type="text/javascript">
        document.write('hello world');
    </script> -->


    <!-- js引入方法二:外部js引入 -->
    <!-- <script src="js/js01.js"></script> -->


数据类型:

  • 原始数据类型,存在于栈
  1. string 字符串
  2. boolean
  3. number
  4. undefined
  5. null
  6. symbol 符号,es6新增的一种数据类型,使用let a = Symbol('bbb'),独一无二
  • 引用数据类型
  1. Object
  2. Array
  3. Date
  4. Function
  5. RegExp

       <!-- 原始值 stack(栈) 栈相当于裤兜,先放进去的最后出来
       var num=100;
       num1=num;新开一个房间把值的副本复制过来
       num=200;


       引用值 引用的是地址 heap(堆) 值存堆里面,栈里面存的是堆的地址

       var arr=[1,2];
       arr1=arr;直接指向arr的地址
       arr.push();插入一个值
       arr=[1,3];开一个新房间


       错误:
       低级错误,程序直接一行都不执行,如果掺杂中文字符

       逻辑错误,哪出现错误后面的都不执行,前面会执行,如变量未定义


       html页面中的各script是相通的,可以互相引用,但一个script中出现错误不会影响其他script,但是只能后面的script引用前面的script


数值运算符
       NaN not a number NaN不等于自己
       运算
       任何字符串运算最后都是字符串
       +可以做数学运算也可以链接字符串,运算方法是自左向右
       -、*、/、%、=最弱、()最强,++,-- ,+=,-=
       ++ --在前先运算然后执行语句
       ++ --在后先执行语句然后运算

       var a= 100;
       var b= 200;
       交换ab之间的值
       方法一借助第三个参数c
       var c=a;
       var a=b;
       var b=c;

       方法二通过加减运算

       a=a+b;
       b=a-b;
       a=a-b;

       1 / 0  infinity无穷大
       0 / 0  NaN
       -1 / 0  -infinity无穷大

比较运算符  运算值是Boolean值

字符大小比较的是ascll码  A是65  a是97  数字0是48  字符串是一个一个比

        
        <  >  ==  >=  <=  !=


逻辑运算符  

        && 返回原本的值,先判断第一个值是真是假,如果是真就直接返回第二个值,如果第一个是假就直接返回这个假的值
        直接返回假的,到假就返回,没假返回最后的
        &&前面是真才会执行后面的,可以作为一种控制语句使用,取名短路语句
        碰到假就返回

        2 && 1

        
        ||
         先判断第一个值,如果是真就直接返回,如果是假就判断第二个值,返回的都是判断的值
         碰到真就返回
         2||1
         适用于兼容性,ie浏览器取值windows.event,其他浏览器取值e,其实两个值是一样的,因为浏览器差异而导致的,解决此问题就可以使用||碰到真的就返回
        undefined null NaN "" 0 false  ===>false


        !非运算,返回值是Boolean值

        /*
        计算2的n次幂
        var sum=1;
        var n = window.prompt('计算2的n次方,请输入自然数n');
        for(;n > 0;n--){
            sum *= 2;
        }
        document.write(sum);

        for(var i = 0; i<n; i ++){}
        */
        

        /*
        var n = window.prompt('计算n的阶乘,请输入自然数n');
        var sum = 1;
        for(;n > 0;n --){
            sum = sum * n;
        }
        document.write(sum);

        for(var i = 1; i<=n; i ++){}
        */

        /*
        第n项的斐波那契值
        var f1 = 0;
        var f2 = 1;
        var f3 = 0;
        var n = window.prompt('计算第n项的斐波那契值,请输入自然数n');
        for(;n > 0;n --){
            f3 = f1 + f2;
            f1 = f2;
            f2 = f3;
        }
        document.write(f1);
        */

        

        /*
        反向输出3位数n
        var n = window.prompt('反向输出n,n是3位数,请输入自然数n');
        a = n % 10 * 100;
        b = parseInt(n / 10) % 10 * 10;
        c = parseInt(n / 100);
        document.write(a+b+c);
        */


        


        /*
        比较输入的3个数大小
        var a = window.prompt('请输入数字a的值');
        var b = window.prompt('请输入数字b的值');
        var c = window.prompt('请输入数字c的值');
        if(a > b && a > c){
            document.write(a);

        }else if(b > a && b > c){
            document.write(b);

        }
        else{
            document.write(c);
        }


        if(a > b){
            if(a > c){
                document.write(a);
            }
            else{
                document.write(c);
    
            }
            }
            else{
                if(b>c){
                    document.write(b);
                }
                else{
                    document.write(c);
                }
            }
        */
// window.οnlοad=function (){
//      var a = parseInt(window.prompt('请输入数字a的值')); 
//         var b = parseInt(window.prompt('请输入数字b的值'));
//         var c = parseInt(window.prompt('请输入数字c的值'));
//         console.log(typeof a);
//         // console.log(a);
//         // console.log(b);
//         // console.log(c);
//         if(a > b){
//          if(a > c){
//              document.write(a);
//          }
//          else{
//              document.write(c);
//          }
//         }else{
//          if(b > c){
//              document.write(b);
//          }
//          else{
//              document.write(c);
//          }
//         }
//     };
       


        // if 满足条件后还会去判断
        // else if满足条件后不去判断其他的
        // else

        /*
        for (var i = 100; i --; ) {
            document.write(i+' ');
        }
        for语句>>>先执行一遍,然后判断2执行3,判断里面是有if的

        
        while(条件){
            执行语句
            条件变化
        }

        do{
            执行语句
        }while(条件)
        */
         

        // Math.sqrt();//开方函数

/*
        var n = 1;
        switch(n){
                case 1: 
                console.log("a");break;
                case 2: 
                console.log("b");break;
                case 3: 
                console.log("c");break;

        }

        break;//功能是终止循环
        continue;//功能是终止本次循环,进行新一次循环
*/
        //数组

        /*
        var arr = [0,1,11,4,5,5,30];
        //arr[0]
        //arr[0] = 1
        for(var i = 0; i < arr.length; i ++){
            console.log(arr[i]);
        }
        */


        //对象
        /*
        var obj = {
            name : "nick",
            age : 20,
            father : "big nick"

        }

        console.log(obj.name);
        obj.name = "old nick";
        console.log(obj.name);
        */

        /*


        1.面向过程  机械性  一步一步做

        2.面向对象  依赖解决问题的方法
        */

        /*


        typeof()//分析数据类型 number string Boolean object(数组,null,对象) undefined function

        1.显示类型转换
            Number('123');//转换成数字,看起来不是数字的字符串无法转成数字number类型,undefined会转成NaN看起来不是数字的字符串会转成NaN,null会转成0
            parseInt();//把数值转换成整型
            parseInt(demo,16);//把值按进制转换成相应数值
            parseInt("123abc");//可以取出数字123
            parseFloat();//转换成浮点型
            parseInt("123.222abc");//可以取出数字123.222
            String()//转换成字符串类型
            Boolean()//转换成布尔类型
            toString()//转换成字符串,null和undefined不能使用

            isNaN();//会把括号里面的值放到Number()里面然后跟NaN对比,隐式调用Number

            null==undefined都不是数字

            NaN自己不等于自己NaN

            ===绝对等于
            !==绝对不等于


            变量未定义会报错,只有一个情况不报错就是该未定义变量放在typeof()里面不报错返回值是undefined
            typeof()返回值是字符串 number string boolean object(数组,null,对象) undefined function都是字符串

            */
            //console.log(Number(null));//返回0
            
            /*
            var a = "123";
            a ++;//会调用Number()把a转成数字

            'a' + 1//隐式转换字符串
            == !=//有隐式转换
            */
          
            //console.log(typeof(a+""));
            //var a="abc123";
            //alert(typeof(undefined));
           // alert("11"+11);
           //alert(typeof(typeof(a)));
           //var num = 1231230.123456;
           //alert(num.toFixed(3));
           //var a = 10 + "20";
           //document.write(a);
           alert(NaN==undefined);

           num.tofixed(3);//保留3位小数并四舍五入


            
        //函数  function  高内聚 低偶合 定义一个功能
        //定义函数
        function test(){

        }
        test();//调用函数

        函数命名规范  小驼峰  第一个单词小写后面的单词首字母都大写

        test代替是整个函数体

        第二种函数定义 

        命名函数表达式  abc是函数名字,test代表函数体
        test.name = abc
        var test = function abc(){

        }

        匿名函数表达式 ---  函数表达式  demo代表函数体和函数名
        var demo = function () {

        }


        //sum(a,b)里面的ab表示形式参数---形参  相当于var a,b;
        function sum(a,b){
            var c = a + b;
        }

        //sum(1,2)里面的叫实际参数----实参
        sum(1,2);

        //形参和实参不需要数量上对应,可多可少
        //函数里面有个叫实参列表的东西叫arguments数组,他会把实参全部放到这个数组里面,arguments.length
        //形参里面也有一个存形参的功能,直接是函数名,也是个数组sum.length
        //形参多于实参时,有多少实参就映射多少个实参,实参列表出生的时候有多少就是多少不会再改变

        //return第一个功能是终止函数,第二个是返回值

        function sum(){
            return 123;
        }
        //要定义一个变量来接收返回值
        var a = sum();

         //写一个函数计算任意个数的和
        var result = 0;
        function sum(){
            for(var i = 0;i < arguments.length;i ++){
                result += arguments[i];
            }
            console.log(result);

        }
        sum(1,2,3,4,5,6,7,8,9);

        //写一个属于自己的转换函数,把字符串转换成数字
        function myNumber(target){
            return +target;
        }
        var num = myNumber('123');
        console.log(num);


        //写一个函数,功能是告知你所选定的动物的叫声
        /*
        function talk(select){
            switch(select){
                case 'A': document.write('汪汪');break;
                case 'B': document.write('嚎嚎');break;
                case 'C': document.write('喵喵');break;

            }
        }
        var select = window.prompt('A.狗 B.狼 C.猫 请输入您的选择');

        talk(select);
        */

        //实现加法计数器
        /*
        function add(a,b){
            var c = a + b;
            return c;

        }
        var a = parseInt(window.prompt('请输入第一个数'));
        var b = parseInt(window.prompt('请输入第二个数'));
        console.log(add(a,b));
        */


        //计算n的阶乘
        /*
        function moreMul(n){
            var sum = 1;
            for(var i = 1;i <= n;i++){
                sum = sum * i;

            }
            document.write(sum);

        }
        moreMul(4);
        */

        //计算n的阶乘方法二  此方法叫递归

        /*
        function jc(n){
            if(n == 1){
                return 1;
            }
            return n * jc(n - 1);
        }
        */


        //函数实现斐波那契数列
        /*
        function feibo(n){
            if(n<3){
                document.write(1);
            }
            else{
            f1 = 1;
            f2 = 1;
            for(var i = 2;i < n;i ++){
                f3 = f1 + f2;
                f1 = f2;
                f2 = f3;
            }
            document.write(f3);
        }
        }
        feibo(1);

        //求斐波那契数列的方法二 递归
        */
        function feibo(n){
            if(n == 1 || n == 2){
                return 1;
            }
            return feibo(n - 1) + feibo(n - 2);

        }
        console.log(feibo(7));


        //递归方法规律,找数据规律,找出口,一定要指定出口,没出口就会一直死循环
        //出口指的是亘古不变的东西
        //递归的先执行的最后返回,因为他要等其他的计算完返回

        //字符串也可以一个一个拿出来,相当于数组,可以一个一个取str[],还有个专用的charAt()


        //输入数字然后以该数字的中文逆转输出
        /*
        function reverse(){
            var num = window.prompt("请输入数字");
            var str = '';
            for(var i = num.length - 1;i >= 0;i --){
                str += transfer(num[i]);
            }
            document.write(str);
        }
        function transfer(target){
            switch(target){
                case "1" :
                     return '一';
                case "2" :
                     return '二';
                case "3" :
                     return '三';
                case "4" :
                     return '四';
                case "5" :
                     return '五';
                case "6" :
                     return '六';
                case "7" :
                     return '七';
                case "8" :
                     return '八';
                case "9" :
                     return '九';
                case "0" :
                     return '零';

            }

        }
        reverse();
        */

        //全局变量  在js里面定义的变量  整个都可以使用
        //局部变量  函数里面定义的的变量 函数里面才能使用,外面不能使用
        //外面拿不到里面的,函数1里面的函数2,函数1不能拿到函数1的变量
        //并行行数之间的变量也不能访问


        js解析性语言(语法分析---预编译(全局预编译,函数预编译)---解释执行)

        js三部曲
        1.语法分析,通篇看一下代码看看有没有低级错误
        
        2.预编译  提前调用test()  提前输出前面未定义的变量a  这些都是预编译过程
        test();
        function test(){
            console.log('a');

        }
        console.log('a');
        var a = 123;


        预编译发生在函数执行的前一刻
        预编译四部曲
        1.创建AO对象(Activation Object) 执行期上下文 活跃对象
        AO{

        }

        2.找形参和变量声明,将形参和变量名作为AO对象的属性名,值都为undefined

        3.形参和实参值相统一,就是把实参的值放到形参里面去

        4.在函数体里面找函数声明,函数名作为属性名,函数体作为值


        //同名的变量和形参函数声明,函数声明权限最高
        //函数里面有预编译,全局也有预编译,全局的叫GO,window===GO
        //先GO然后AO,AO上有就使用AO,没有就往GO找,遵循近者优先
        //判断时要看清函数在哪开始执行

        

        //预编译总结:
        //1.函数声明整体提升,函数出生起就被拿到了最前面去,所以无论在哪调用他都可以执行
        //2.变量 声明提升

        3. 解析执行

        imply global//暗示全局变量  a = 10;默认是全局变量

        window.a = 123;//全局对象   表示window{a : 123}

        变量都是window全局对象,都可以使用window.b访问
        var b = 123;可以使用window.b访问
        //window就是全局的域
        一切定义在全局的变量都归window所有
        未经声明就给他赋值的变量默认是全局变量
        window就是全局
        window就是一个对象
        var a = 123;
        var b = 234;
        var c = 567;
        相当于
        window{
            a : 123,
            b : 234,
            c : 567
        }

        console.log(a);相当于console.log(window.a);


        作用域  域scope

        每个js函数都是一个对象,对象有些属性我们可以访问,但有些是不可以的,这些属性仅给js引擎存取,[[scope]]就是其中一个

        运行期上下文:当函数执行时,会创建一个执行期上下文的内部对象,一个执行期上下文定义了一个函数执行时的环境,函数每次执行时对应的上下文都是独一无二的,所以多次调用一个函数会导致创建多个执行上下文,当函数执行完毕,它所产生的执行上下文被销毁

        [[scope]]指的就是我们所说的作用域,其中存储了运行期上下文的集合

        作用域链:[[scope]]中所存储的执行期上下文对象的集合,这个集合成链式链接,我们把这种链式链接叫做作用域链

        函数定义的时候就有GO了,他就存在于a.[[scope]]里面第一个就是-----> 0 : GO{}
        函数执行的时候a.[[scope]]就发生变化了,第一个存的是-------------> 0 : AO{}
                                              第二个存的是--------------> 1 : GO{}

        查找变量的时候是从作用域链顶端开始查找 

        闭包
        多个函数对应的闭包都是同一个,划线模型在巨人的基础上调用
        当内部函数被保存到外部时,将会产生闭包。闭包会导致原有作用域链不释放,造成内存泄露 

        内存泄露就是内存被占用

        闭包的作用
        1.实现公有变量,比如累加器
        function add(){
            var count = 0;
            function demo(){
                count ++;
                console.log(count);
            }
            return demo;
        }
        var counter = add();
        counter();

        2.可以做缓存(存储结构)

        /*
        例一
        function test(){
            var num = 100;
            function a(){
                num ++;
                console.log(num);
            }
            function b(){
                num --;
                console.log(num);
            }
            return [a,b];
        }
        test();

        例二

        function eater(){
            var food = "";
            var obj = {
                eat : function (){
                    console.log("I am eating " + food);
                    food = "";
                },
                push : function (myFood){
                    food = myFood;
                }
            }
            return obj;
        }
        var eater1 = eater();
        eater1.push('banana');
        eater1.eat();
        */


        3.可以实现封装,属性私有化           后面课
        闭包作用 私有化变量 除非自己说有,别人不可能知道他有
       function Person(name){
        var money = 100;
        this.name = name;
        this.makeMoney = function(){
            money ++;
        };
        this.offer = function (){
            money --;
        }
        this.say = function(){
            console.log("I have money" + money);
        }
       }
       var person = new Person();


        4.模块化开发,防止污染全局变量       后面课
        var name = "bac";
        var init = (function (){
            var name = "abc";
            function callName(){
                console.log(name);
            }
            return function (){
                callName();
            }//形成闭包,留一个接口等待外面被调用,还不会污染全局变量,这个就是变量私有化
        }())
        init();


        

        //针对初始化功能的函数也就是正常的函数,需要多次使用的函数

        //立即执行函数  执行完一次就被销毁了 所以无需取名  可以有返回值  可以直接用变量接收整个函数
        (function (形参){
            var a = 123;
            var b = 1747;
            console.log(a + b);
        }(实参))

        扩展:只有表达式才能被执行符号执行  ()执行符号  表达式被执行后就忽略了函数名
        变成表达式的方法 var test = function..   +function  -function !function

        立即执行函数表达式被执行后,就会失去对原有函数的索引 test变成undefined了
        var test = function (){
            console.log("a");
         }();

         函数声明跟函数表达式要分清,函数声明是不能立即执行的,会报错,只有表达式才能被执行
         function test(){
            console.log("a");
         }();

         立即执行函数表达式被执行后,就会失去对原有函数的索引,函数名会失去索引
         function test(){
            var arr = [];
            for(var i = 0;i < 10;i ++){
                (function (j){
                    arr[j] = function (){
                        console.log(j);
                    }
                }(i))

            }
            return arr;
         }
         var myArr = test();
         for(var j = 0; j < 10; j ++){
            myArr[j]();
         }

        //函数声明和函数表达式是两个东西
        //
        //把整个函数闭包到外部
        把函数赋值给变量,系统不会看函数里面,就知道把函数给这个变量,除非要访问这个变量,他才会去执行这个变量里面的东西
        function test(){
            var arr = [];
            for(var i = 0; i < 10; i ++){
                arr[i] = function (){
                    console.log(i);
                }
            }
            return arr;
        }
        var myArr = test();
        for(var j = 0; j < 10; j ++){
            myArr[j]();
        }

        //arr[i] = function (){console.log(i);}这个是把整个函数存到arr里面,而不会去立马执行这个function (){console.log(i);}
        
        闭包

        当内部函数被保存到外部时,将会产生闭包。闭包会导致原有作用域链不释放,造成内存泄露

        闭包示例  让里面函数跑到外面去 return 该函数   把改函数赋值给全局变量
        function a(){
            function b(){
                var bbb = 234;
                document.write(aaa);
            }
            var aaa = 123;
            return b;
        }

        var glob = 100;
        var demo = a();
        demo();


        闭包会导致原有作用域链不释放,造成程序内存空间被占用,专业词是造成内存泄露

        累加器
        function test(){
            var count = 0;
            function a(){
                console.log(++ count)
            }
            return a;
        }
        var myAdd = test();
        myAdd();

        全局变量放到GO
        函数定义,形参和局部变量定义放到AO,函数权限最高


        逗号运算符 他会运行表达式1然后运行表达式2最后返回表达式2的结果
        (表达式1,表达式2)

        ()运算符会把里面的东西变成表达式,用完一次直接会丢失索引,而且()权限最高

        数字加上一个不能进行运算的字符会变成NaN

        对象  是一种基础的数据类型  任何事都可以抽象成一个对象 对象可以有属性和方法 对象上的方法就是函数

        var  mrDeng = {
            name : "MrDeng",
            age : 40,
            sex : "male",
            health : 100,
            smoke : function (){
                console.log('I an smoking !');
                mrDeng.health --;//mrDeng==this
            },
            drink : function(){
                console.log('I an drinking !');
                mrDeng.health ++;//mrDeng==this
            }
        }

        属性的增加,修改,删除,查询

        增加一个属性,直接mrDeng.属性名 = "";
        删除需要借助delete 属性名


        对象的创建方法
        1.var obj = {}    plainObject   对象字面量,对象直接量
        2.构造函数  有new就会生成一个对象
                1.系统自带的构造函数  Object()  var obj = new Object();
                2.自定义

        //大驼峰试命名规则  构造函数遵循大驼峰命名规则
        function Person(){}
        var person = new Person();

        function Car(color){
            this.color = color;
            this.name = 'BMW';
            this.height = "1400";
            this.lang = "4900";
            this.weight = 1000;
            this.health = 100;
            this.run = function(){
                this.health --;
            }
        }
        var cae = new Car("red");
        var car1 = new Car("black");

        用户自选car的颜色,就可以新加一个参数放到函数里面

        构造函数内部原理  构造函数有了new他就可以生产函数
        有new就会出现下面3步
        1.在函数体最前面隐式加上this = {}  创建空的this对象
        2.执行this.xxx = xxx;
        3.隐式返回this

        出现new就是构造函数了,new出来的东西就是对象了

        new 操作的详细:
            1.创建一个空对象obj={}
            2.设置对象obj的constructor属性为构造函数,并将obj的proto属性指向构造函数的原型
            3.调用构造函数并将构造函数的this指向新对象
            4.隐式返回一个obj对象实例

        var num = new Number(123);//对象数字123    跟原始值数字是有区别的
        var str = new String("abcd");//对象字符串abcd 
        var bol = new Boolean('true');//对象布尔

        原始值数字和字符串、布尔是不能有属性和方法的,但对象数字、字符串、布尔是可以有属性和方法的

        //包装类
        var num = 4;

        num.len = 3;//原始值是不能进行像对象那样的操作,但系统还是不会报错,他还会给你new Number(4).len = 3;但系统这样做完后他会自动进行delete操作,把他又删除了

        console.log(num.len);//依然原始值还是没有对象操作,但系统还是会隐式的给你new Number().len

        上面的过程就叫做包装类

        字符串和数组是系统自带length属性的
        Number没有这个属性

        大函数里面的小函数只要被弄出了大函数就会产生闭包,产生了闭包,小函数就一直拿着大函数的AO和GO
        this和new配合也可以把一个函数弄出来,因为this里面隐式有return

        parseInt(3,8);//把3看成是8进制的3转换成十进制

        形参和相映射,牵一发而动全神,形参改会改,实参改也会改

        原型定义:原型是function对象的一个属性,它定义了构造函数制造出的对象的公共祖先。通过该构造函数产生的对象,可以继承该原型的属性和方法, 原型也是对象

        原型 prototype 他也是个对象 从函数出生起就有这个属性了
        Person.prototype      ---原型
        Person.prototype = {}  ---是祖先
        只要是对象就可以进行增删改查操作 Person.prototype.name = 'heheh';
        所以通过person()构造出的函数就可以继承该对象属性  
        原型是公有属性  该构造函数构造出的对象都可以继承该公有属性
        函数自己也可以新建与原型里面名字相同的属性,构造函数构造出的函数就会拿函数自己有的,而不会去拿原型里面的   遵循就近原则

        function Person(){

        }
        var person = new Person();

        

        通过原型还可以解决代码冗余  把大家都共有的属性提取出来放到原型对象里面

        function Car (color){
            this.name = "BMW";//公有属性 就可以放到祖先prototype对象原型里面,解决代码冗余
            this.height = 1400;//公有属性 就可以放到祖先prototype对象原型里面,解决代码冗余
            this.length = 4000;//公有属性 就可以放到祖先prototype对象原型里面,解决代码冗余
            this.color = color;
        }
        var car = new Car("red");

        原型里面的东西是比较难修改的,除非通过Person.prototype修改,其他方法是不可能的

        new 出来的对象继承的原型是不能删除的,删除后还是存在,除非操作prototype


        4.对象如何查看对象的构造函数-----------> constructor  构造出对象后就会自带一个constructor属性
        constructor属性是存在原型prototype对象里面里面的一个属性,他存储着构造函数整个函数体

        原型里面的属性只能通过对象去修改如Car.prototype.属性 = "";

        大驼峰对象函数 
        function Person (){
            //var this = {
            //      __proto__ : Person.prototype
            //}
        }

        即__proto__  === Person.prototype

        但是仅仅是说__proto__和prototype两个人指向的房间(对象)是一样,new的时候才会找__proto__,new了之后对象就被存到变量里面,相当于__proto__是属性而属性值是prototype

        原型里面套原型就组成了原型链  原型链连接点就是__proto__ 就近  
        Object.prototype是原型链的最终端
        原型值只有本人有权限修改删除和增加,其他人没有,查询权限大家都有

        Grand.prototype.lastName = "Deng";
        function Grand(){

        }
        var grand = new Grand();

        Father.prototype = grand;
        function Father(){

        }
        var father = new Father();

        Son.prototype = father;
        function Son(){

        }
        var son = new Son();

        sayName里面的this指向是,谁调用的这个方法,this就是指向谁

        示例
        Person.prototype = {
            name : "a",
            sayName : function (){
                console.log(this.name);
            }
        }
        function Person(){
            this.name = "b";
        }
        var person = new Person();


        对象定义  下面两个是一样 正常都是使用第一个  两个都是有原型的
        var obj = {}----对象字面量,对象直接量----系统内部就会给你new Object()
        var obj = new Object()


        Object.prototype是原型链的最终端
        __proto__里面是 Object.prototype

        创建对象的另一个灵活方法 -- var obj = Object.create(原型);
        示例1
        var obj = {name : "sunny",age : 12};
        var obj1 = Object.create(obj);
        示例2
        Person.prototype.name = "sunny";
        function Person(){

        }
        var person = Object.create(Person.prototype);


        绝大多数对象最终都会继承自Object.prototype
        特例是
        Object.create(null);它里面就没有原型

        var num = 123;//数字是原始值,他是不能进行对象操作的,
        num.toString();//数字进行toString()方法操作,他调用是包装类new Number(num).toString();他使用的是Number对象里面的Number.prototype里面的toString方法

        方法的重写  函数名字一样,重新给他写个功能
        示例  toString本来是原型里面就有的方法,通过修改后就是方法的重写
        Person.prototype = {
            toString : function (){
                return 'hehhe';
            }
        }
        function Person(){

        }
        var person = new Person();

        示例2
        Object.prototype.toString = function (){
            return "haha";
        }

        Object.prototype.toString
        Number.prototype.toString
        Array.prototype.toString
        Boolean.prototype.toString
        String.prototype.toString

        Object.prototype.toString.call(123);

        document.write();//调用的是函数里面的toString()方法

        Math.floor();//向下取整函数12.1232325 = 12
        Math.ceil();//向上取整函数12.1212 = 13
        Math.random()//区间(0,1)随机函数
        toFixed(2);//保留2位小数并且四舍五入

        bug bug导致原因是js本身精度不准导致的   一般用Math.floor取整 浮点数都存在精度不准的bug js运算精度前16位后16位
        0.14 * 100 = 14.00000000002

        可计算方法小数点前16位后16位

        任何一个方法都可以.call
        test();======test.call();这两个是一样的
        test();默认函数里面的this指向window

        var obj = {}
        test.call(obj);这样写的话就改变了this指向,指向obj,所有call的作用就是用来改变this指向

        function Person(name,age){
            //this == obj
            this.name = name;
            this.age = age;

        }
        var person = new Person("deng",100);
        var obj = {};
        Person.call(obj,'cheng',300);//call改变this指向obj,函数里面就变成obj.name,obj.age,obj对象里面就加入了cheng和300,借用了别人的方法来给自己做事

        借用别人已有的 call本质是借用别人的函数来实现自己的功能 想用别人的方法就call他
        function Person(name,age){
            this.name = name;
            this.age = age;

        }
        function Student(name,age,grade,sex){
            Person.call(this,name,age);
            this.grade = grade;
            this.sex = sex;
        }

        


        造车

        function Model(height,width,len){
            this.height = height;
            this.width = width;
            this.len = len;
        }
        function Sit(sitConfortable,sitColor){
            this.sitConfortable = sitConfortable;
            this.sitColor = sitColor;
        }
        function.Wheel(style){
            this.style = style;
        }
        function Car(height,width,len,sitConfortable,sitColor,style){
            Model.call(this,height,width,len);
            Sit.call(this,sitConfortable,sitColor);
            Wheel.call(this,style);
        }
        var car = new Car(1000,1000,1000,"牛皮舒服","red","八大金刚轮胎");


        call需要把实参按形参的个数传进去
        apply需要穿一个arguments实参列表以数组形式
        两者功能是一样的,都是改变this指向,不同之处就是传参列表不同

        函数都有原型 原型链

        继承发展史  继承原型上的属性

        1.传统形式  儿子继承父亲,父亲继承祖父,造成儿子也继承了祖父,但过多的继承导致继承了很多不要的属性
        示例
        Grand.prototype.lastName = "Deng";
        function Grand(){

        }
        var grand = new Grand();

        Father.prototype = grand;
        function Father(){

        }
        var father = new Father();

        Son.prototype = father;
        function Son(){

        }
        var son = new Son(); 


        2.借用构造函数  call和apply  缺点是不能继承借用构造函数的原型 每次构造函数都要多走一个函数  视觉上是缩减了代码 但运行上是多走了
        function Model(height,width,len){
            this.height = height;
            this.width = width;
            this.len = len;
        }
        function Sit(sitConfortable,sitColor){
            this.sitConfortable = sitConfortable;
            this.sitColor = sitColor;
        }
        function.Wheel(style){
            this.style = style;
        }
        function Car(height,width,len,sitConfortable,sitColor,style){
            Model.call(this,height,width,len);
            Sit.call(this,sitConfortable,sitColor);
            Wheel.call(this,style);
        }
        var car = new Car(1000,1000,1000,"牛皮舒服","red","八大金刚轮胎");


        3.共享原型
        Father.prototype.lastName = "deng";
        function Father (){

        }
        function Son(){

        }
        Son.prototype = Father.prototype;
        var son = new Son();

        写一个函数功能实现两个构造函数共享原型
        function inherit(Son,Father){
            Son.prototype = Father.prototype;//两个同时指向一个房间,一个改都会改
        }


        4.圣杯模式  借助一个中间函数
        function inherit(Target,Origin){
            function F(){}//借助中间函数
            F.prototype = Origin.prototype;
            Target.prototype = new F();
            Target.prototype.constructor = Target;//归位
            Target.prototype.uber = Origin.prototype;//添加一个属性到时候方便自己知道继承的是谁
        }
        Father.prototype.lastName = "deng";
        function Father (){

        }
        function Son(){

        }
        inherit(Son,Father);
        var son = new Son();

        执行以上后
        son.__proto__  ---> new F().__proto__ ------->Father.__proto__
        所以son.constructor是Father所以为了归位一下son.constructor = Son;

        函数被保存到了外部,他就存储了刚才执行上下文

        function Deng(name,wife){
            var prepareWife = "xiaozhang"
            this.name = name;
            this.wife = wife;
            this.divorce = function (){
                this.wife = prepareWife;
            }
            this.changePropareWife = function (target){
                prepareWife = target;
            }
            this.sayPrepareWife = function (){
                console.log(prepareWife);
            }
        }
        var deng = new Deng();

        以上Deng函数里面的3个函数被保存到了外部形成了闭包,这就导致了这3个函数拿着Deng的AO和GO到了外部,又因为prepareWife是一个变量而不是属性,只能Deng里面才有这个变量,deng是继承不到这个变量,deng只能继承属性,这就导致了别人是无法知道deng里面是不是有这个变量,这就把prepareWife做了很好的隐藏,这个就是闭包的第三个作用,变量私有化


        命名空间 也是一个对象
        作用:管理变量,防止污染全局,适用于模块化开发

        var org = {
            department1 : {
                jicheng : {
                    name : "abc";
                    age : 12
                },
                laodeng : {

                }
            },
            department2 : {
                zhangsan : {
                    name : "aaa",
                }
            }
        }

        var jicheng = org.department1.jicheng;
        jicheng.name就可以使用了
        org就是命名空间

        var name = "bac";
        var init = (function (){
            var name = "abc";
            function callName(){
                console.log(name);
            }
            return function (){
                callName();
            }//形成闭包,留一个接口等待外面被调用,还不会污染全局变量,这个就是变量私有化
        }())
        init();


        如果函数不写return那就是默认return undefined

        方法的连续调用就可以return this;就可以连续调用了

        var obj = {
            name : "abc"
        }
        obj.name  =====  obj['name']两个是一样的功能,使用obj.name的时候系统会自动隐式转换为obj['name']
        obj[]里面必须是字符串,这样就可以在里面进行字符串拼接,使用就更加灵活了

        对象的枚举  遍历就是挨个拿出来这个过程就是个遍历过程,相当于枚举

        对象是没有length属性的,所以就无法用for循环一个一个遍历出来 对象枚举的时候会把原型也遍历出来,如果原型里面有属性和值,解决办法是判断一下prop是不是自己的属性,方法是obj.hasOwnProperty(prop)用来过滤一下,因为for的时候都不希望拿到原型里面东西
        所有for in循环都会搭配hasOwnproperty()方法

        属性是字符串,没加''""是变量

        in可以判断是否在obj上 "height" in obj;


        

        解决办法是

        var obj = {
            name : "nbi",
            age : 12,
            sex : "male",
        }

        for(var prop in obj){
            console.log(obj.prop);//obj.prop隐式变成obj['prop'],系统就回去找prop这个属性因为prop本来就是一个字符串
        }
        var prop in obj//系统会把obj里面的属性名传到prop里面去


        instanceof
        A instanceof B  官方解释instanceof判断A对象是不是B构造函数构造出来的,但真正是看A对象的原型链上有没有B的原型

        区分一个变量是什么类型的3个方法

        方法0
        typeof

        方法1
        变量.constructor会返回他的构造函数

        方法2
        变量 instanceof 类型(Array Object)会返回 true false

        方法3
        调用toString方法,toString()方法是Object.prototype里面的一个方法,他的返回值也可以知道变量是什么类型 
        Object.prototype.toString.call({})
        Object.prototype.toString.call([])

        ()运算符会把里面的东西变成表达式,用完一次直接会丢失索引

        引用值比较的是地址,看是不是指向一个房间

        实参列表arguments存储在AO里面,this : window也是存在AO里面

        this
        
        var a = 5;
        function test(){
            a = 0;
            alert(a);
            alert(this.a);
            var a;//左右是把a提到AO里面,本来a是全局的本应该在GO里面
            alert(a);
        }
        test();结果是0 5 0
        new test();结果是 0 undefined 0
        如果出现new test();this也发生着改变,系统内部会var this = {
            __proto__ : test.prototype
        }


        1.函数预编译过程this指向window

        2.全局作用域里面this指向window

        3.call和apply可以改变函数的this指向

        4.谁调用了该函数,this就指向谁,obj.a,obj调用函数a,this就指向obj 

        没人调用直接执行就是预编译 a.say代表函数体a.say()代表执行say方法

         var name = "222";
         var a = {
            name : "111",
            say : function(){
                console.log(this.name);
            }
         }
         var fun = a.say;
         fun();
         a.say();
         var b = {
            name : "333",
            say : function (fun){
                fun();
            }
         }
         b.say(a.say);
         b.say = a.say;
         b.say();

         arguments 上有length和callee属性

         1.arguments.callee指向函数自身引用

         var num = (function (n){
            if(n == 1){
                return 1;
            }
            return n * arguments.callee(n - 1);//求(n-1)的阶乘,但无函数名,所以只能借函数引用arguments.callee
         }(20));

         2.arguments.caller指代的是被调用的环境,返回被调用的环境,返回被谁调用,整个返回
         function test(){
            demo();
         }
         function demo(){
            console.log(demo.caller);
         }


         属性有原始值和引用值之分,数组、对象是引用值

        克隆 
        原始值拷贝是不出问题的,但如果里面有数组,数组是引用值,使用的是地址,指向的是同一个房间,就会导致其中一个改另一个就会跟着改  这种克隆叫浅层克隆
        浅层克隆示例:
        var obj = {
            name : "abc",
            age : 13,
            sex : 'female'
        }
        var obj1 = {}
        function clone(origin,target){
            for(var prop in origin){
                target[prop] = origin[prop];
            }
            return target;
        }
        clone(obj,obj1);

        深度克隆 支持数组对象的克隆,克隆后不会因为一个改变而跟着改变,是独立分开的

        function deepClone(origin,target){
            var target = target || {},
                toStr = Object.prototype.toString,
                arrStr = "[object Array]";
            for(var prop in origin){
                if(origin.hasOwnProperty(prop)){
                    if(origin[prop] !== 'null' && type(origin[prop]) == 'object'){
                        if(toStr.call(origin[prop]) == arrStr){
                            target[prop] = [];
                        }else{
                            target[prop] = {};
                        }
                        deepClone(origin[prop],target[prop]);
                    }else{
                        target[prop] = origin[prop];
                    }
                }
            }
            return target;
        }

        三目运算  条件判断?是:否 并且会返回值
        if(toStr.call(origin[prop]) == arrStr){
                            target[prop] = [];
                        }else{
                            target[prop] = {};
                        }
        变成三目运算
        target[prop] = toStr.call(origin[prop]) == arrStr ? [] : {};


        字符串比较会比ascll码
        变量定以后不给他赋值会提示undefined

        数组  也是引用值  来源于Array.prototype 数据类型是对象 任意操作,基本不报错,只会undefined

        var arr = [];        数组字面量[]里面传什么数据就是什么数据
        
        var arr = new Array();构造函数定义数组,如果括号里面只传了一个参数他会默认是是数组长度,传1位必须是整数,长度没有小数,传小数报错

        数组常用方法(es3.0)  数组自带方法  改变原数组

        1.push往数组最后一位开始添加数据,可以同时多个数据添加
        push方法
        Array.prototype.push = function (){
            for(var i = 0; i < arguments.length;i ++){
                this[this.length] = arguments[i];
            }
            return this.length;
        }

        2.pop()把最后一位剪切出来,只能一位

        3.shift在数组最前面删除数据

        4.unshift在数组最前面添加数据

        5.reverse把数组里面的数据逆转

        6.splice(从第几位开始,截取多少的长度,在切口处添加新数据)切片,会返回切出来的

        7.sort排序  按ascll码排序  数组里面如果是多位数字那就不能满足我们的排序 sort预留了一个接口给我们自己写方法实现
        var arr = [1,2,5,12,23];

        方法规则添加要求
        1.必须写两个形参
        2.看返回值 sort规则
                1.当返回值为负数时,那么前面的数放在前面
                2.为正数,那么后面的数放前面
                3.为0,不动
        var arr = [1,2,4,45,35,12,47]
        方法会把前两位数据传进去,然后两个数进行比较,根据返回值排序所以直接a-b就是返回值
        升序return a-b
        降序return b-a
        arr.sort(function(a,b){
            if(a > b){
                return 1;
            }else{
                return -1;
            }
        });

        乱序
        var arr = [1,2,5,12,23];
        arr.sort(function(a,b){
            return Math.random() - 0.5;
        });

        sort()接口原理,把前两个传进去,可以传数组对象,进去后看返回值,如果为正数,后面的放前面,如果为负数,前面的放前面

        var deng = {
            age : 19,
            name : "niubi"
        }
        var cheng = {
            age : 45,
            name : "jicheng"
        }
        var zhang = {
            name : "zhang",
            age : 66
        }
        var arr = [deng,cheng,zhang];
        arr.sort(function(a,b){
            return a.age - b.age;
        }); 

        不改变原数组  那变量来接收
        var arr = [1,2,3,4,5];
        var arr1 = [6,7,8,9];

        1.concat连接 arr.concat(arr1)

        2.toString把数组变成字符串

        3.slice(从该位开始截取,截取到该位)
          slice(正数从该位开始截取到最后一位)
          slice(负数截取倒数几位)
          slice(不写)截取所有

        4.join('字符串类型') 把数组里面的东西按指定符号连接起来返回一个字符串

        5.split("按什么符号拆分") 按什么符号把字符串拆分成数组,

        var str1 = 'alibaba';
        var str2 = 'baidu';
        var str3 = 'tencent';
        var str4 = 'toudiao';
        var arr = [str1,str2,str3,str4];
        arr.join("");


        类数组 如arguments  把数组和对象的特性都拼接到一起,使用更灵活,类数组必须要有length,属性要为数字索引,方法需要而外添加进去,如数组本应该有的方法push,splice方法都需要而外加进去他才有这个方法

        类数组示例 特征要求:属性要为索引(数字)属性,必须要有length属性,最好加上push属性
        var obj = {
            "0" : 'a',
            "1" : 'b',
            "2" : 'c',
            "length" : 3,
            "push" : Array.prototype.push,
            "splice" : Array.prototype.splice
        }

            1.可以利用属性名模拟数组特性

            2.可以动态的增长length属性

            3.如果强行让类数组调用push方法

        数组push原理
        Array.prototype.push = function(target){
            this[this.length] = target;
            this.length ++;
        }

        模拟typeof()方法
        function type(target){
            var template = {
                "[object Array]" : "array",
                "[object Object]" : "object",
                "[object Number]" : "number - object",
                "[object Boolean]" : "boolean - object",
                "[object String]" : "string - object"
            }
            if(target === null){
                return "null";
            }else if(typeof(target) == "object"){
                var str = Object.prototype.toString.call(target);
                return template[str];
            }else{
                return typeof(target);
            }

       }


       数组去重  借用对象里面属性唯一
       var arr = [1,2,3,1,1,4,2,2];
       Array.prototype.unique = function (){
            var temp = {};
            arr = [];
            len = this.length;
            for(var i = 0;i < len;i ++){
                if(!temp[this[i]]){
                    temp[this[i]] = 'abc';//如果abc改成this[i]那就无法去掉0,因为重复0,!0是true
                    arr.push(this[i]);
                }
            }
            return arr;
       }


       复习

       包装类:
       原始值包括正常的string number存储都在栈里面,没有属性,
       但字符串是自带length属性的,
       原始值字符串它隐式了new String(),这个隐式的过程叫包装类
       原始值数字你给他加个属性他不会不错,原因是他也隐式了new Number().属性,但加完后他会给你删了,等于没执行,这个过程也叫作包装类

       new一个构造函数返回值是对象
       
       引用值 如数组,对象 ,函数,自带属性


       原型
       任何一个函数都会有一个prototype,prototype是这个函数的祖先

       function Person(){

       }
       var person = new Person();
       new Person()后他就会看自己有没有祖先上的属性,如果没有那就继承祖先的东,如果有就用自己的,没有就用祖先的
       new Person()的时候隐式产生了
       var this = {
        __proto__ : Person.prototype
       }


       var obj = Object.create(原型)里面必须放原型(对象),然后在新建出的对象里面的原型指向demo即__proto__ : demo
       var demo = {
        lastName : "deng"
       }
       var obj = Object.create(demo);//即obj的原型指向demo
       obj = {
        __proto__ : demo
       }


       var num = 123;
       一旦经过了var的操作,所得出的属性,window,这种属性叫做不可配置属性,不可配置的属性是不能delete的

       delete只能删除可配置的属性,如obj.num = 123;是可以删除的
       var obj = {}
       obj.num = 123;

       call/apply作用是改变this指向,区别是传参列表不一样
       call/apply应用实例一
       function Person(name,age){
        this.name = name;
        this.age = age;
       }
       function Student(name ,age, sex){
        //var this = Object.create(Student.prototype);
        Person.call(this,name,age);//使用Person()实现Student上的功能,就要把this指向Student
        this.sex = sex;
       }


       call/apply应用实例二
       function test(){

       }
       test() == test.call()在系统内部中执行test()的时候系统会自动变成test.call(),如果里面放参数,参数可以是对象,可以是window,this就会指向该参数,存于AO里面 


       this的4个特点
       1.预编译过程this指向window
       2.全局作用域里面this指向window
       3.谁调用this指向谁
       4.call和apply可以改变this指向


       闭包
       A函数里面有个B函数,通过各种手段把B保存到了A外部就形成了闭包,B就拿着A的AO和GO

       闭包作用 私有化变量 除非自己说有,别人不可能知道他有
       function Person(name){
        var money = 100;
        this.name = name;
        this.makeMoney = function(){
            money ++;
        };
        this.offer = function (){
            money --;
        }
        this.say = function(){
            console.log("I have money" + money);
        }
       }
       var person = new Person();

       [] + "" 会等于 ""

       [] == [] 虽然名字相同但是两个房间,指向是不一样的

       function (a){
            var a;//形参相当于在函数里面var了一下a
       }

       表达式里面的函数名是无效的 表达式执行后就会丢失对函数名的索引
       var h = function a(){
        return 23;
       }

       表达式执行后就会对表达式里面的东西丢失索引

       JavaScript中的数据类型分为原始值和引用值
       原始值包括number string undefined boolean null symbol
       引用值包括Array Object Function


       作业
       1.一个字符串a-z组成,找出该字符串第一个出现的,只出现一次的字母
       2.字符串去重


       try里面发生错误就会停止执行,错误前面的代码会正确执行,try外面的也会执行
       catch的作用是捕捉错误里面只有两个参数name和message

       try{

       }catch(e){
            e.message  e.name
       }


       1.EvalError:eval()的使用与定义不一致

       2.RangeError:数组越界

       3.ReferenceError:非法或者不能识别的引用数值

       4.SyntaxError:发生语法解析错误

       5.TypeError:操作数类型错误

       6.URIError:URI处理函数使用不当


       es严格模式

       目前浏览器都是运行基于es3.0 + es5.0的新增方法 目前如果es3.0和es5.0冲突了是按es3.0的标准执行,如果要用es5.0执行那就要开启es5.0

       要启用es5.0就需要启用严格模式  "use strict"必须要把命令写在第一行,可以写在全局里面。那么整个页面都遵循es5.0,也可以写在局部函数里面,那么就这个函数遵循es5.0,推荐使用局部

       很明显的是es5.0不支持arguments.callee方法

       开启方式为什么不使用函数来开启,因为老版本的浏览器可能都没有更新到es5.0并没有这个函数,这样页面就直接报错了,而使用字符串在老版本浏览器上最多就是表达式没用,并不会报错


       es5.0不能使用的语法

       1.with(){}可以改变作用域链,他里面的语句会把AO指向with()指定的AO,with()里面放对象
       var obj = {
        name : "lisi"
       }
       var name = "zhangsan";
       function test(){
        var name = "wangwu";
        with(obj){
            console.log(name);
        }
       }


       with的作用用处 简化代码 命名空间的正确使用需要配合with
       var obj = {
        dp1 : {
            jc : {
                name : "abc",
                age : 123
            },
            deng : {
                name : "nnn",
                age : 111
            }
        },
        dp2 : {

        }
       }

       with(obj.dp1.jc){
        console.log(name);
       }

       document是一个对象
       with(document){
        write('a');
       }

       with(){}把作用域链都改变了,所以es5.0不能使用的语法


       2.arguments.callee,caller等一些方法在严格模式下不能使用了


       3.严格模式下规定变量赋值前必须声明

       4.严格模式下局部this必须要赋值,不赋值的话就是undefined,即在严格模式里局部的默认this是不指向window的

       5.es5.0里面拒绝重复的参数和属性名  重复的参数会报错,重复的属性不报错
       function test(name,name){
            console.log(name);
       }
       test(1,3);

       var obj = {
        name : 123;
        name : 11123;
       }


       6.eval()他可以执行代码  es3.0都规定不能使用  他会改变作用域链

       什么DOM


       1.DOM Document Object Model  文档对象模型

       2.DOM定义了表示和修改文档所需的方法。用来操作HTML和xml功能的一类对象的集合,也有人称DOM是堆html和xml的标准编程接口,DOM不能操作css文件样式表,只是改的行间样式


       xml----xhtml---HTML4.0--HTML5.0发展历程,现在xml都被json取代


       var div = document.getElementsByTagName('div')[0]
       div.style.height = "100px";
       div.style.width = "100px";
       div.style.backgroundColor = "red";


       document代表整个文档,document上面有很多方法

            var div = document.getElementById('');//id是唯一的
            var div = document.getElementsByTagName('')[];//类数组,所有浏览器都可以使用
            var div = document.getElementsByClassName('')[]//在老的浏览器不支持比如ie8以下
            var div = document.getElementsByName()//理论上只有部分标签有效,如表单里面的、img
            
            以下2个并不使用  不实时 是静态的  选完一次后就不变,是副本,新加的不会选进去  受局限
            var div = document.querySelector('')//跟css一样选取,选的是一个,ie7以下没有
            var div = document.querySelectorAll('')//跟css一样选取,选的是一组,放数组里面,ie7以下没有

        css、html里面id用的少,大部分都使用class


        遍历节点数 var div = document.getElementsByTagName('div')[0]
        div.parentNode   父节点,最顶端的parentNode是document  每个元素都有这个属性
        div.childNodes[i]   子节点们  
        div.firstChild   第一个子节点
        div.lastChild    最后一个子节点
        div.nextSibling  后一个兄弟节点
        div.previousSibling 前一个兄弟节点


        节点类型
        元素节点---1   文本节点---3 注释节点---8  document---9


        基于元素节点数的遍历  除了children其他的在IE9及IE9以下不兼容
        
        parentElements  返回当前元素的父元素节点,是元素,document不是元素
        children[i]  元素子节点  常用
        firstElmentChild 第一个元素子节点
        lastElementChild 最后一个元素子节点
        node.childElementcount  === node.children.length 当前元素子节点的数量
        nextElementSibling 下一个兄弟元素节点
        previousElementSibling 前一个兄弟元素节点

        每一个节点都有的四个属性
            nodeName  返回元素的标签名,只能读取不能修改
            nodeValue 只有文本节点和注释有这个属性,可以修改和读取
            nodeType  返回该节点的类型,只能读取,返回值是1.2.3.8.9
            attributes 元素节点的属性集合,是个类数组
            <div id="demo" class="demo1"></div>
            div.attributes[0].nodeType返回值就是属性节点的返回值2
            div.attributes[0].name
            div.attributes[0].value

        每个节点都有一个方法 hasChildNodes()方法判断是否有子节点


        Document是一个系统留给自己的构造函数,他不能new
        document是代表整个文本,继承自HTMLDocument,HTMLDocument继承自Document
        HTMLDocument.prototype = {

            __proto__ : Document.prototype
        }

        DOM结构树  表示的就是继承关系  最顶端是Node 最终还是继承自Object

        Node--------Document--------HTMLDocument-----document
        Node--------CharacterData--------Text/Comment
        Node-------Element----------HTMLelement----HTMLHeadElement/HTMLBodyElement/HTMLTitleElement/...
        Node----Attr


        1.getElementById()是Document.prototype上的一个方法
        
        2.getElementByName()是HTMLDocument.prototype上的方法
        
        3.getElementsByTagName()是定义在Document.prototype和Element.prototype上的方法
        var div = document.getElementByTagName('div')[0];
        var span = div.getElementByTagName('span')[0];

        var div = document.getElementByTagName('*')[0];//选中所有标签

        4.HTMLDocument.prototype上定义了一些常用的属性,如body、head分别指代HTMLBodyElement、HTMLHeadElement
        默认选好了body和head,直接使用document.body/document.head


        5.document.documentElement----------->html

        6.getElementsByClassName、querySelector、querySelectorAll在Document.prototype和Element.prototype上均有定义


        DOM基本操作
            1.增 
                创建一个元素节点,创建标签
                var div = document.createElement('div');
                document.body.appendChild(div);
                div.innerHTML = 123;

                创建文本节点
                var text = document.createTextNode('邓哥');

                创建注释节点
                var comment = document.createComment();

            2.插入  任何元素节点都有appendChild方法 插入到父元素里的最后面
                
                PARENTNODE.appendChild
                var div = document.getElementsByTagName('div');
                var text = document.createTextNode('laodeng');
                div.appendChild(text);把页面中已有的东西插入到另一个地方就是剪切操作,如果div里面之前有其他元素或者文本会保留,然后把新的插入到其后面

                PARENTNODE.insertBefore
                var div = document.getElementsByTagName('div');
                var span = document.getElementsByTagName('span');
                var strong = document.createElement('strong');
                div.insertBefore(strong,span);

            3.删除
                parent.removeChild();  做的是剪切操作
                div.removeChild(i)

                
                child.remove
                i.remove(); 自尽,真销毁,没了


            4.替换
                parentNode.replaceChild(new,origin)拿新的替换老的,也是剪切

                var strong = document.createElement('strong');
                div.replaceChild(strong,p);

    Element节点上的一些属性
        1.innerHTML
        div.innerHTML = '';//改变div里面的内容,他的作用是改变元素里面的内容,如果只元素里面有内容就会被覆盖,他还可以进行运算,比如保留之前的内容,然后把新内容追加放到后面,他取出来的是html标签,所以也可以写html标签代码进去div.innerHTML = "<p style='background: red;color: white;'>123</p>";双引号里面放单引号,不能双引号里面放双引号,会报错

        2.innerText 火狐浏览器里面没有这个方法,但它提供了textContent跟这个方法一样,其他浏览器也有textContent,但IE不支持textContent
        
        他会取他里面的所有文本
        他还可以赋值,如果他里面还有结构,赋值后会覆盖之前的结构,变成赋值内容


    Element节点上的一些方法
        1.ele.setAttribute()给元素设置一个行间属性
        ele.setAttribute('class','demo');
        作用:可以先写好一个带class的样式,然后通过ele.setAttribute()给他加上,还可以通过if条件触发这个效果

        <div></div>
        <strong></strong>
        <p></p>
        给上面3个元素添加一个属性,属性名是this-name,值是他的标签名nodeName

        var all =document.getElementsByTagName('*');
        for(var i = 0;i < all.length;i ++){
            all[i].setAttribute('this-name',all[i].nodeName);
        }

        2.ele.getAttribute()获取元素的一个属性,返回是属性的值
        ele.getAttribute('id')


        如果要修改一个元素的class和id可以直接赋值覆盖,
        如div.className = ''  div.id = ''


    1.日期对象 系统提供好的 两个属性constructor,prototype
        var date = new Date();

        Date构造函数构造出的函数方法  基于那一时刻记录的时间
            Date()返回当日的日期和时间
            date.getDate()返回一个月的第几天
            date.getDay()返回一周的第几天,从0开始,星期天开始0
            date.getMonth()返回第几个月,从0开始,加1就OK
            date.getFullYear()返回年份
            date.getYear()有误会出现118,推荐使用date.getFullYear(),因为很早之前是6位时间,99.12.31出问题了
            minutes、seconds、hours返回那一刻的时分秒
            getTime()返回1970.01.01至今的的时间戳,毫秒数

            var date = new Date();
            date.setDate(15)设置date记录的是当月的第15天和那时刻的时分秒
            设置一个时间,触发某代码

            示例
            var date = new Date();
            date.setMinutes(40);
            setInterval(function(new Date().getTime()-date.getTime() > 1000){
            console.log('你很帅');
            },1000);


            date.setTime(45454464111),从1970.01.01开始毫秒
            date.toString()把时间转换成字符串

        计算机的纪元年是1970.01.01


            js定时器

                1.setInterval(代码,时间毫秒);循环执行里面的代码,只要没有清除,就不会停止,一直按时间循环,时间确定后永远不会变,就算后面改变也是无效的,定时器的精确度不是很准,setInterval()是window上的方法,每一个计时器都会有一个唯一标识,第一个计时器就是1,返回值是1
                示例
                setInterval(function(){},1000);
                setInterval("console.log("a")",1000);也可以是双引号括起代码,一样意思,没人用

                2.clearInterval();清除计时器 通过接受setInteval的唯一标识,是个数字从1开始,清除就是靠这个数字来进行的
                示例
                var timer = setInterval(function (){
                console.log(i++);
                if(i > 10){
                clearInterval(timer);
                }
                },10)

                3.var timer = setTimeout(function(){},1000);他的作用是在1000毫秒后执行,而且只执行一次

                4.clearTimeout(timer),清除计时,直接不执行了

                以上方法都是window上的方法,this都是指向window

            BOM基本操作

                1.查看滚动条观点距离
                    window.pageXOffset  X轴移动距离,实际距离=首屏宽度+X,IE8及以下不支持
                    window.pageYOffset  Y轴移动距离,实际距离=首屏高度+Y,IE8及以下不支持

                IE8及以下使用的是 兼容性十分混乱 其中一个方法有效,另一个方法就是0
                    document.body.scrollLeft/Top  IE8 IE5 IE4
                    document.documentElement.scrollLeft/Top  IE7 IE6

                    所以解决兼容性的办法就是2个方法相加
                    document.body.scrollLeft + document.documentElement.scrollLeft
                    document.body.scrollTop + document.documentElement.scrollTop


                封装一个完整的方法
                function getScrollOffset{
                    if(window.pageXOffset){
                    return {
                        x : window.pageXOffset,
                        y : window.pageYOffset
                    }
                }else{
                    return {
                        x : document.body.scrollLeft + document.documentElement.scrollLeft,
                        y : document.body.scrollTop + document.documentElement.scrollTop
                    }
                }
                }

            2.查看视口尺寸 就是可视窗口大小

                window.innerWidth/innerHeight返回值是像素,IE8及以下不兼容

                IE8及以下使用的是
                    标准模式下任意浏览器可用方法
                        document.documentElement.clientWidth/clientHeight

                    怪异模式下的浏览器方法
                        document.body.clientWidth/clientHeight

                    浏览器有2种渲染模式:标准模式、怪异模式(混杂模式),渲染指的是把html翻译过来呈现出来,页面最前有DTD文档类型声明就是标准模式,如果没有就会启动混杂模式

compat--兼容性

                封装一个完整的方法

                function getViewportOffset(){
                    if(window.innerWidth){
                        return {
                            w : window.innerWidth,
                            h : window.innerHeigth
                        }
                    }else{
                        if(document.compatMode === 'BackCompat'){
                            return {
                                w : document.body.clientWidth,
                                h : document.body.clientHeight
                            }else{
                                return {
                                    w : document.documentElement.clientWidth,
                                    h : document.documentElement.clientHeight
                                }
                            }
                        }
                    }
                }


        3.查看元素尺寸和位置  方法一和二求的都是元素可视的大小,包括padding

            方法一 不常用
            1.es5.0的新方法,返回结果不是实时的,而是求的那一时刻的静态写照,后期改变大小或者位置,他求的是不会改变的,静态保存,不会动态更新
            
            2.位置求的是左上顶点和右下位置的顶点坐标
            
            3.ele.getBoundingClientRact();//结果里面一共6个数据,top、bottom、left、right、元素height、width,此方法在IE里面求出来只有4个参数,没有height和width,间接使用right-left和bottom-top可以求到
                

            方法二 常用方便 任何一个元素都可以使用
                1.查看元素尺寸的方法ele.offsetWidth ele.offsetHeight此方法求的是元素实际可视的大小,包括padding
                2.查看元素位置ele.offsetLeft/Top/Right/Bottom 求的是相对于父级的位置,前提是有父级定位,如果没有父级定位求的是相对文档的距离

                注意:横向margin会相加,纵向的margin会塌陷重合到padding或者内容区域里面,position的默认值是static


                ele.offsetParent求的是返回有定位的父级的坐标位置,如果没有有定位的父级就返回body

        4.让滚动条滚动
            window.scroll(X,Y)   window.scrollTo(X,Y) 两个方法都是一样的,作用是使滚动条滚动到要求的位置,不能多次累加

            window.scrollBy(X,Y) 此方法可以多次累加

    脚本化CSS  通过DOM间接操作css  

        1.ele.style所有元素都有这个方法,他会返回一个样式表,类数组,可以读写里面的数据,所以就可以直接修改,而且是实时更新,只有这一种方法可以读取和写入操作的方法,只此一种,他操作的是行间样式,不在行间就看不到,所有浏览器都支持
            ele.style.width = "";修改值必须是字符串,
            ele.style.backgroundColor = "";//用-组合的属性,js里面没有-符号,用小驼峰代替
            ele.style.cssFloat = "left";//float比较特殊,他是保留字,所以w3c推荐改成cssFloat,但float本身也可以
            ele.style.border = ""//

        2.window.getComputedStyle(ele,null); 计算而来 没有相对值,只有绝对值10em=160px  
            1.此方法根据样式的权重获取,谁的权重高就获取谁的,得到的是最终的结果,他只可以查询,不能修改
            2.IE8及以下不兼容,他使用ele.currentStyle.width
            3.null是用来获取伪元素的属性


            封装兼容方法

            function getStyle(ele,prop){
                if(window.getComputedStyle){
                    return window.getComputedStyle(ele,null)[prop];
                }else{
                    return ele.currentStyle[prop];
                }
            }
       
       3.变成思想
            想改变一个特殊的元素的css,可以给他再次创建一个class样式,到时候把改该元素的class改成新创建的class,把值赋给他就可以达到实现,直接要修改的操作先预先写好css然后改className
            比如
                .yellow::after{
                    content : "";
                    width : 10px;
                    height : 10px;
                    background : yellow;
                    display : inline-block; 
                }
                .green::after{
                    content : "";
                    width : 10px;
                    height : 10px;
                    background : green;
                    display : inline-block; 
                }

            4.position的left和top默认值是auto,是个相对值

    事件  交互触发效果

        绑定事件处理函数
         1.ele.onclick = function(){} 此方法兼容性很好,但是一个元素同一事件只能绑定一个,相当于属性赋值,如果再绑定一个就会覆盖之前的,此方法就相当于写在行间了,也可以直接写在行间,onclick = "代码"

         2.ele.addEventListener(type,function,false)此方法IE9以下不兼容,可以绑定多个事件处理函数,如果处理函数相同那么只执行一次,按事件绑定顺序执行

         3.IE独有 ele.attachEvent('onclick',function(){});此方法也是可以绑定多个处理函数,绑定几次就执行几次,可以绑定相同的函数

         注意:绑定事件的时候如果用了for循环,就要考虑闭包了,特别是循环给一个元素添加事件的时候,因为一直用着i
         示例
         <script>
    var li = document.getElementsByTagName('li');
    var div = document.getElementsByTagName('div')[0];
    div.addEventListener('click',function(){
        alert('牛逼');
    },false);
    for(var i = 0;i < li.length;i ++){
        li[i].addEventListener('click',function(){
            console.log(i);
        },false);
    }
    </script>

        解决办法就是使用加一个立即函数


        事件的this:指代的都是哪个元素绑定的事件,this就指向哪个元素,ele.onclick ele.addEventListener(type,function,false)都是指向ele,
        但IE的ele.attachEvent('onclick',function(){});指向是window,
        要让他指向ele的示例
            div.attachEvent('onclick',function(){
                hander.call(div);
            });
            function hander(){
                //  事件处理
            }

            封装事件绑定兼容性方法
                function addEvent(ele,type,handle){
                    if(ele.addEventListener){
                        ele.addEventListener(type,handle,false);
                    }else if(ele.attachEvent){
                        ele.attachEvent('on'+type,function(){
                            handle.call(ele);
                        });
                    }else{
                        ele['on'+click] = handle;
                    }
                }


        解除事件  不需要要这个事件了
            1.ele.onclick = null;

            2.ele.removeEventListener('click',原函数引用,所以当时必须把事件处理函数拿到外面写,留下引用接口,不然无法解除,false);//写函数名即可,如果写test()会立即执行,写索引就可以了

            3.ele.detachEvent('on'+type,原函数引用,所以当时必须把事件处理函数拿到外面写,留下引用接口,不然无法解除);//写函数名即可,如果写test()会立即执行,写索引就可以了

        事件处理2个模型 如果两个都存在先事件捕获后事件冒泡
        focus、blur、change、submit、reset、select等事件不冒泡
          1.事件冒泡 代码结构存在父子关系就会存在事件冒泡即同一事件,自子元素冒泡向父元素
                <div class="wrapper">
                    <div class="content">
                        <div class="box"></div>
                    </div> 
                </div>
                自box---content---wrapper
                <script>
                    var wrapper = document.getElementsByTagName('div')[0];
                    var content = document.getElementsByTagName('div')[1];
                    var box = document.getElementsByTagName('div')[2];
                    wrapper.addEventListener('click',function(){
                    console.log('wrapper');
                    },false);

                    content.addEventListener('click',function(){
                        console.log('content');
                    },false);

                    box.addEventListener('click',function(){
                        alert('box');
                    },false);

                </script>
            注意:事件冒泡是存在代码结构上的,不是视觉效果上的 


          2.事件捕获  只有Chrome一款浏览器有 从外面开始 自父元素捕获至子元素  最新款浏览器基本都有但IE没有,触发顺序,先捕获后冒泡
                <div class="wrapper">
                    <div class="content">
                        <div class="box"></div>
                    </div> 
                </div>
                自wrapper---content---box
                <script>
                    var wrapper = document.getElementsByTagName('div')[0];
                    var content = document.getElementsByTagName('div')[1];
                    var box = document.getElementsByTagName('div')[2];
                    wrapper.addEventListener('click',function(){
                    console.log('wrapper');
                    },true);

                    content.addEventListener('click',function(){
                        console.log('content');
                    },true);

                    box.addEventListener('click',function(){
                        alert('box');
                    },true);

                </script>

            注意:IE上特有的事件获取,也叫事件捕获,通过ele.setCapture()把所有事件都捕获到自己身上,要释放的时候使用ele.releaseCapture()


        3.取消冒泡和阻止默认事件
            1.事件对象,点击事件函数里面可以放一个形参,系统会把事件对象的所有参数数据传到里面,包括很多当前点击的很多数据

            2.w3c标准取消冒泡方法event.stopPropagation();但IE9以下不支持
                示例
                    div.onclick = function(e){
                        e.stopPropagation();
                        this.style.background = "green";
                    }

            3.IE独有方法是event.cancelBubble = true;

            封装取消冒泡方法
                function stopBubble(){
                    if(event.stopPropagation){
                        event.stopPropagation();
                    }
                    else{
                        event.cancelBubble = true;
                    }
                }


            4.阻止默认事件  表单提交  a标签跳转  右键菜单等
                1.阻止鼠标右键
                    document.oncontextmenu = function(e){
                        console.log('a');
                        return false;//句柄的方式
                        e.preventDefault();//常用
                    }

                2.取消a标签默认事件在行间写<a href="javascript:void()"></a>此a标签就是去了跳转功能

                封装一个方法解决阻止默认事件的方法
                    function cancelHandler(e){
                        if(e.preventDefault){
                            e.preventDefault();
                        }else{
                            e.returnValue = false;
                        }
                    }


        1.事件对象 event || window.event用于IE

        2.事件源对象  触发事件的源头
            求事件源对象:event.target  火狐有这个
                          event.srcElement  IE有这个
                          Chrome浏览器两个方法都有
            封装兼容性方法
                ele.onclick = function(e){
                    var event = e || window.event;
                    var target = event.target || event.srcElement;
                    console.log(target); 
                }


        3.事件委托 利用事件冒泡和事件源对象进行处理 本来是儿子完成的任务委托给父亲 子元素冒泡给父元素

            <ul>
                <li>1</li>
                <li>2</li>
                <li>3</li>
                <li>4</li>
                <li>5</li>
                <li>6</li>
                <li>7</li>
                <li>8</li>
                <li>9</li>
                <li>10</li>
            </ul>

            var ul = document.getElementsByTagName('ul')[0];
            ul.onclick = function(e){
                var event = e || window.event;
                var target = event.target || event.srcElement;
                console.log(target.innerText);
            }

            事件委托优点
                1.性能,不需要使用循环,使用循环还有闭包问题,如果循环1亿次呢
                2.灵活,当有新的子元素进来时不需要重新绑定事件


        鼠标事件
            click = mousedown + mouseup
            contextmenu鼠标右键
            mousemove鼠标移动
            mouseover==mouseenter鼠标移上去
            mouseout==mouseleave鼠标移开

        监控鼠标左键右键 mousedown和mouseup可以监测左右键,其他的不行,onclick监测的是左键
            如果一个操作既要实现拖拽又要跳转,实现方法是根据时间差来实现
                <script>
                var firstTime = 0;
                var lastTime = 0;
                var key = false;
                document.onmousedown = function(){
                    firstTime = new Date().getTime();
                }
                document.onmouseup = function(){
                    lastTime = new Date().getTime();
                    if(lastTime - firstTime < 300){
                        key = true;
                    }
                }
                document.onclick = function (){
                    if (key) {
                        console.log('click');
                        key = false;
                    }
                }
                </script>


            1.鼠标右键返回值是2,左键是0

            <script>
                document.onmousedown = function(e){
                    if(e.button == 2){
                        console.log('right');
                    }else if(e.button == 0){
                        console.log('left');
                    }
                }
            </script>

            2.移动端里面的3个事件是
                touchstart
                touchmove
                touchend


        键盘事件
            1.onkeydown 按下
            2.onkeyup   松手
            3.onkeypress 持续按不松手keydown>keypress
            触发顺序 keydown>keypress>keyup

            keydown keypress区别
                keydown可以监测到所有按键,但charCode是0不能判断是哪个按键
                keypress只能监测字符类按键,ASCLL码里面有的字符,charCode会返回ASCLL值
                操作类按键上下左右keydown的which返回值会不一样,他监测的是键盘的排序


        文本操作事件
            1.input事件,文本框里面的内容发生变化就会触发该事件
                var input = document.getElementsByTagName('input')[0];
                input.oninput = function(e){
                console.log(this.value);
                }
            2.change事件,文本框获得焦点和失去焦点前后是否有变化,有变化就触发,没有就不触发
                var input = document.getElementsByTagName('input')[0];
                input.onchange = function(e){
                console.log(this.value);
                }

            3.onfocus获得焦点

            4.onblur失去焦点

        滚动条滚动是window上的事件
            window.scrollX
            window.scrollY
            window.scrollTo(X,Y)

        IE里面没有position:fixed;那就可以使用js给他利用position:absolute给他做出来实现,滚动条滚动了多少就把滚动的距离加到top上

        onload事件也是window上的,页面加载完成后执行触发js,页面所有事情都完成后才执行,包括图片视频样式等所有页面东西,这就会导致效率低速度慢,可以放到容易地方


        每个html文档解析的时候会产生domTree和cssTree,两棵树拼到一起就是一棵渲染树,创建domTree的时候只要确认他是什么标签就把他挂到树上

        json
            JSON是一个静态类,系统提供,类似Math
            最早的传输数据格式是xml
            JSON是一种传输数据的格式,他就是一个对象,换了个词就叫json
            为了跟对象区别,属性名必须加""
            示例
            var obj = {
                "name" : 'deng',
                "age" : 123
            }
            数据传输的过程是把字符串传过去,那就需要把json换成字符串的格式来传输
                把json对象换成字符串系统提供的方法JSON.stringify(obj);
                var str = JSON.stringify(obj);
                后台给前台传输数据也是传输过来的字符串,把字符串转换成对象的方法是JSON.parse(str)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值