2017-03-31 面向对象学习笔记

函数的调用方式和 this 丢失

  • 函数调用方式
  • 普通函数方式调用 this - window
  • 对象的方法 this - 对象
  • 构造函数方式 this - 内部创建的新对象
  • 函数上下文(call|apply) this - 第一个参数
  • this 的指向发生了改变
    函数的调用方式发生了改变
var name = 'window 的属性';
function demo(){
        console.log(this.name);
}
var obj = {
        name:"张三",
        demo:demo
}
demo();
obj.demo();
var div = document.getElementById('demoId');
console.log(div);
//var div2 = getId('demoId');
//console.log(div2);
//document.getElemenetById 方法内部的实现,在该方法内部使用到了 this
//这个 this 默认指向的是document
//getId 在调用的时候使用普通函数的方式来进行调用, this 指向的是 window
document.getId = document.getElemenetById;
var div3 = document.getId('demoId');
console.log(div3);
//console.log(document.getElemenetById.call(document,''demoId));
//getId('demo');
var getId = (function(){
        return function(){
                  //return document.getElementById.call(document,arguments[0]);
                  return document.getElemenetById.apply(document,arguments);
        }
})();
var div4 = getId('demoId');
console.log(div4);

图书管理员面向对象

//注意点:在原型对象的方法中访问该对象的其他方法,需要使用 this 前缀
function BookListManager(){
         this.bookList = null;
}
BookListManager.prototype = {
        constructor:BookListManager,
        init:function(){
                this.bookList = arr || [];
        },
        getBook:function(name){
                for(var i in this.bookList){
                          if(this.bookList[i].name == name){
                                return this.bookList[i];
                          }
                  }
                  throw "要查找的对象不存在!"
        },
        updateBook:function(name,author){
                //先找到这个对象
                var book = this.getBook(name);
                //修改对象
                book.author = author;
        },
        addBook:function(obj){
                this.bookList.push(obj);
        },
        removeBook:function(){
                var book = this.getBook(name);
                var index = this.bookList.indexOf(book);
                if(index == -1){
                        throw '要删除的对象不存在'
                }
                //删除该对象
             this.bookList.splice(index,1);
        }
}
var jack = new BookListManager();
var tom = new BookListManager();
jack.init([{name:'古典文学研究',author:'古典'},{name:'莫言文集',author:'莫言'}]);
jack.addBook({name:'飞鸟集',author:'泰戈尔'});
console.log(jack.getBook('飞鸟集'));
jack.updateBook('飞鸟集','老太太');
jack.removeBook('飞鸟集');
console.log(jack.bookList);

严格模式的简单说明

  • js 中有两种开发模式,非严格模式(默认) + 严格模式 ('use strict')
  • 严格模式会进行更严格的代码检查
  • 以前可以的特性在严格模式下可能被禁止使用
  • 以前可以使用的代码方式,在严格模式下会直接报错
  • 开启严格模式
  • 字符串命令 'use strict';
  • 位置 当前作用域的顶端
  • 兼容性问题:
    严格模式不存在兼容性问题,如果当前的浏览器支持严格模式,那么再扫描到"use strict"命令的时候就会开启严格模式检查,如果不支持严格模式那么就直接忽略
  • 使用建议
    建议在写代码的时候全部开启严格模式

严格模式使用注意

  • 在严格模式下,所有的变量都必须使用 var 声明
  • 在严格模式下,禁止使用八进制
  • 在严格模式下,禁止使用 with
  • 在严格模式下,不能删除全局的变量
  • 在严格模式下,不能在 if 语句中声明函数
  • 在严格模式下,函数的形参不能出现同名的情况
  • 在严格模式下,不能使用 callee|caller
  • 在严格模式下,不能使用 eval 和 arguments 作为标识符(变量和函数名称)
  • 在严格模式下,修正了 this 的指向
  • 在严格模式下,arguments 的表现不一致
  • 在严格模式下,对象中不能出现同名的属性
    "use strict";

    //01 在严格模式下,所有的变量都必须使用var声明
    //   在默认情况下,如果不适用var声明变量,那么该变量默认会成为window的属性(全局变量)
//    var a = 10;
//    b = 20;
//    console.log(b);

    //02 在严格模式下,禁止使用八进制
//    var num = 022;            //数值以0开头,以八进制的方式来处理
//    var num = 0x22;
//    console.log(num);

    //03 在严格模式下,禁止使用使用with
//    var obj = {name:"张三",age:20};
//    with (obj)
//    {
//        name = "李四";
//        age = 99
//    }
//    console.log(obj);

    //04 在严格模式下, 不能删除全局的变量
    //   在默认情况下,可以删除全局变量(不能删除成功),静默失败
//    var str = "string";
//    console.log(delete str);
//    console.log(str);

    //05 在严格模式下,不能在if语句中声明函数
//    if (true)
//    {
//        function demo() {
//            console.log("demo");
//        }
//
//        demo();
//    }

    //06 在严格模式下,函数的形参不能出现同名的情况
//    function test(a,b,a) {
////        var a = 1;
////        var b = 2;
////        var a = 3;
//        console.log(a + b + a);   //6 ? 8 ? 4  后面的会把前面的覆盖
//    }
//
//    test(1,2,3);

    //07 在严格模式下,不能使用arguments.callee|caller
    //caller 指向的函数的调用者 注意点:window调用该函数,指向的是null
    //arguments.callee 常用在递归调用中,指向的是函数自己
//    function f1() {
//        f2();
//    }
//    function f2() {
//        console.log(f2.caller);
//    }
//    f1();
//    f2();

//    console.log((function (n) {
//    if (n == 1) {
//        return 1;
//    }
//
//    return arguments.callee(n - 1) + n;
//})(11));

    //08 在严格模式下, 不能使用eval和arguments作为标识符(变量和函数的名称)
//    var eval = "测试字符串";
//    console.log(eval);
//    var arguments ="....";
//    console.log(arguments);

    //09 在严格模式下,修正了this的指向
    //   默认情况下,this指向的是window,严格模式指向的undefined
//    function func() {
//        console.log(this);
//    }
//
//    func();
//    var o = {};
//    func.call(null);            //严格模式下,指向的是null
//10 在严格模式下,arguments的表现不一致
    //在默认情况下,如果函数内部形参被重新设置,那么arguments也会跟着改变
    //在严格模式情况下,如果函数内部形参被重新设置,那么arguments不会被改变,他们是相互独立的
    //值类型的数据作为函数的参数
    function demo(str) {
        console.log(str);
        console.log(arguments[0]);


        //重新设置形参的值
        str = "hahahaha";
        console.log(str);
        console.log(arguments[0]);
    }
    demo("123456");

    //引用类型的数据作为函数参数
    function demo(obj) {
        console.log(obj);
        console.log(arguments[0]);

        //重新设置形参的值
        obj = {age:20};
        console.log(obj);
        console.log(arguments[0]);
    }
    demo({name:"张三"});

严格模式的书写格式

//"use strict";
//"use strict"               正确
//'use strict';     正确
//'use strict'      正确
//use strict;       错误
//"use Strict";     错误
//"use  strict";    错误
//"use strict ";    错误
//" use strict";      错误

严格模式的作用范围

  • 位置:当前作用域的最上面
  • js 中的作用域:
  • script 标签 全局作用域
  • 函数内部 局部作用域
//    "use strict";    //位置01  对整个作用域中的代码都有用

   function demo01() {
//       "use strict";    //位置02   仅仅对当前的函数有用,函数后面的代码不受影响
       a = 10;
       console.log(a);
   }

   function demo02() {
//       "use strict";    //位置03  仅仅对当前的函数有用
       b = 20;
       console.log(b);
   }

   demo01();
   demo02();

作用域说明

  • 作用域
    概念:变量或者是函数起作用的范围
  • js 的作用域
  • js 本身没有块级作用域(try-catch 除外)
  • js 中只有函数可以创建作用域
  • js 本身是此法作用域 (with|eval 除外)
  • 词法作用域:
    当代码写好之后,某个变量的作用域就已经确定了
  • 动态作用域:
    变量的作用域在代码运行之前是不确定的,只有在代码执行的时候才能根据当前的上下文来确定
  • 词法作用域的访问规则:
  • 单向性的(单向镜),内部的作用域可以访问外层的作用域空间,反过来却不行
  • 在访问变量的时候,先在当前作用于中查找,如果找不到那么就在上一级作用域中查找,重复这个过程
  • 在分析输出的时候,需要考虑到变量和函数声明的提升
  var a = "test-A";
    function f1() {
        var b = "test-B";

    }
    
    function f2() {
        var c = "test-C";
    }

  for (var i = 0; i < 10; i++) {
        console.log(i);
    }

    console.log(i,"_____");             //? 10

    try
    {
        //可能出错的代码
        a();
    }catch (error){
        //如果出错了,那么就执行此处的代码
        console.log(error);
    }
//    console.log(error);
  var demo = "测试的字符串01";
    function f1() {
        var demo = "demo";
        var test = 10;
        f2();
    }

    function f2() {
        console.log(demo);
        console.log(test);
    }

//    f2();   //测试的字符串
    f1();   //demo

变量和函数的提升

  • js 代码的执行
    编译语言
    解释语言(js)
  • 预先解析阶段
    变量和函数声明的提升
  • 具体的执行代码
  • js 变量和函数声明提升的注意点
  • 变量和变量同名 后面的变量会把前面的变量覆盖
  • 函数和函数同名 覆盖
  • 变量和函数同名 函数声明会正常的提升,而变量的声明可以认为被忽略了
    只会提升到当前作用域的最顶端
  console.log("_____");
    console.log(a);            //und
    var a = "test-A";
    console.log(a);            //Test-A
    var a = "demo-A";
    console.log(a);            //demo-A

    //模拟变量声明的提升
//    var a;
//    console.log("_____");
//    console.log(a);            //und
//    a = "test-A";
//    console.log(a);            //Test-A
//    a = "demo-A";
//    console.log(a);            //demo-A
</script>
<script>

    f1();                      //Demo
    function f1() {
        console.log("Test");
    }
    f1();                      //Demo
    function f1() {
        console.log("Demo");
    }
    f1();                       //Demo



    //模拟提升
//    function f1() {
//        console.log("Demo");
//    }
//    f1();                      //Demo
//    f1();                      //Demo
//    f1();
</script>

<!--<script>-->
    <!--console.log(a);                    //函数-->
    <!--var a = "test-A";-->
    <!--function a() {-->
        <!--console.log("demo-A");-->
    <!--}-->
    <!--console.log(a);                   //函数-->



    <!--//模拟-->
    <!--var a;-->
    <!--function a() {-->
        <!--console.log("demo-A");-->
    <!--}-->
    <!---->
    <!--console.log(a);                    //函数-->
    <!--a = "test-A";-->
    <!--console.log(a);                   //test-A-->
<!--</script>-->

<script>
    console.log(a);
    var a = "test-A";
    function a() {
        console.log("demo-A");
    }

//    不管函数在前面还是变量在前面,打印出来的结果都是函数
</script>

思考:
<script>
    var demoA ="10";
    function foo() {
        console.log(demoA);            //10  undefined(正确)
        var demoA = "20";
        console.log(demoA);            //20
    }

    function func() {
        console.log(demoA);            //10
        demoA = "30";
        console.log(demoA);           //30
    }

    foo();
    func();

    console.log(demoA);                 //30

变量提升是分作用域的

  • 在代码执行之前,会把所有的变量和函数生命进行提升
  • 在提升的时候,变量和函数声明的提升是分作用域的,只能提升到当前作用域的顶端
  • 内层作用域中的变量声明并不会覆盖外层作用域中的同名变量

    console.log(demo);
    var a = "第一个a";
    function demo() {
        console.log(a);   //? undefined
        a = "张三";
        console.log(a);   //张三
        var a = "哈哈哈";
        console.log(a);   //哈哈哈
    }

    console.log(a);
    demo();
    console.log(a);     //第一个a(正确) ? 张三  ?哈哈哈
</script>

<script>
    var b = 10;
    function test() {
        console.log(b);
        var b = 20;
    }

    console.log(b);    //10
    test();            //und
    console.log(b);
</script>

函数表达式的提升

  • 函数表达式提升,在提升的时候仅仅只会把声明部分(var func02)提升到当前作用域的顶端
<script>
    console.log(func01);;
    function func01() {
        console.log("func01");
    }
</script>

<script>
//    var func02;
    console.log(func02);;
    var func02 = function () {
        console.log("func02");
    }
</script>

笔试题练习

<script>
    function foo() {
        var num = 123;
        console.log(num);  //123
    }
    foo();
    //console.log(num);       //报错

</script>

<script>
    var scope = "global";
    foo();
    function foo() {
        console.log(scope);  //?global
        scope = "local";
        console.log(scope);  //?local
    }

    console.log(scope);         //local
</script>

in:检查对象中是否存在指定的属性
<!--<script>-->
    <!--function f1(){-->
        <!--var a;-->
        <!--if("a" in window){-->
            <!--a = 10;-->
        <!--}-->
        <!--console.log(a); //? 10 ? undefined ?报错-->
    <!--}-->

    <!--f1();-->

<!--</script>-->

<!--<script>-->
    <!--if("a" in window){-->
        <!--var a = 10;-->
    <!--}-->
    <!--console.log(a); //?  10 -->
<!--</script>-->

<!--<script>-->
    <!--if(!"a" in window){-->
        <!--var a = 10;-->
    <!--}-->
    <!--console.log(a); //?-->
<!--</script>-->



<script>
    if("a" in window){
      a = 10;
    }
    console.log(a); //?  报错
</script>

<script>
    var foo = 1;
    function bar() {
        var foo;
        if(!foo)
        {
            foo = 10;
        }
        console.log(foo);   //?
    }
    bar();
//    undefined == null;  相等
//    undefined == false;  不相等
//    !undefined
    console.log(undefined === null);   //true
    console.log(!undefined == true);
</script>

  function Foo() {
        getName = function(){
            console.log("1");
        };
        return this;
    }
    Foo.getName = function() {
        console.log("2");};
    Foo.prototype.getName = function(){
        console.log("3");};
    var getName = function() {
        console.log("4");
    };
    function getName(){
        console.log("5");
    }
    Foo.getName();      // ? 2
    getName();          // ? 4
    Foo().getName();        // ? (1) ? 3 ? 2 ? 4
    getName();              // ? (1)
    new Foo.getName();      // ? 2
    new Foo().getName();    // ? 3
    new new Foo().getName(); // ? 3

作用域链

  • 有多少个作用域 (函数的个数 + 1)
  • 相同作用域可以相互访问
  • 作用域链:
    在 js 中函数可以创建作用域,在函数内部又可以声明函数,在函数的函数内部又可以声明函数,每个函数都会创建一个作用域,这样就会形成一个作用域链
  • 在访问变量的时候,总是先在自己的作用域中查找
  • 如果没有那么就向上一级作用域查找,如果找到那么就直接使用,如果没有找到那么就继续重复这个过程
  • 直到最外层的全局作用域,如果还没有找到那么就报错
  var a = "a";
    //f1--->全局作用域
    function f1() {
        var b = "b";
        var a = "f1-a"
        //f2-->f1--->全局作用域
        function f2() {
            var c = "c";
            var b = "f2--b";
            //f3-->f2-->f1--->全局作用域
            function f3() {
                var d = "d";

                //f4-->f3-->f2-->f1--->全局作用域
                function f4() {
                    console.log(a, b, c, d,e);
                }

                f4();

            }
            f3();
        }
        f2();
    }

    f1();

作用域链绘图

  • 首先先找出全局变量(包含函数) a f1 f2 f3

  • 画出他们的图形,并且连线(有箭头),箭头的方向表示的是是否可以访问(同一级作用域可以互相访问)

  • 以上画出0级作用域链 var a = "test-a";
    function f1() {
    var b = "test-b";
    function f4() {

          function f5() {
              var c = "test-c";
              var d = "test-d"
          }
      }
    

    }

    function f2() {
    var e = "test-e";

// f4();
}

function f3() {
}
f2();
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,511评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,495评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,595评论 0 225
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,558评论 0 190
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,715评论 3 270
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,672评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,112评论 2 291
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,837评论 0 181
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,417评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,928评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,316评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,773评论 2 234
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,253评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,827评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,440评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,523评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,583评论 2 249

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,049评论 18 139
  • 工厂模式类似于现实生活中的工厂可以产生大量相似的商品,去做同样的事情,实现同样的效果;这时候需要使用工厂模式。简单...
    舟渔行舟阅读 7,615评论 2 17
  • 闭包复习 闭包概念:封闭空间,包裹 闭包的作用:延长变量的声明周期保护内部的数据,代码更安全为我们提供一种间接访问...
    GodlinE阅读 122评论 0 0
  • 一、JavaScript基础知识回顾 1.1 JavaScript 1.1.1 javascript是什么? Ja...
    福尔摩鸡阅读 1,092评论 0 7
  • 眼睛疲惫得都舍不得说晚安。 一点,该睡觉了。 愿你梦里有我。
    hey别为难阅读 344评论 0 1