面向对象第7天

     今天的知识怎么说呢,说多也多,说少也少,有些凌乱。主要的知识点是闭包的三个应用、严格模式的相关知识点、ES5数组新方法、创建对象的方式、继承的方式。下面对各个知识点做详细介绍。   一、闭包的三个应用      1. 参数复用        小案例:音乐模板的js代码       
onload = function() {  //入口函数
            function bindNav(fn) {  
                var lis = document.querySelectorAll("#nav li");
                for(var i = 0, l = lis.length; i < l; i++){
                    lis[i].addEventListener("click", function() {
                        switch(this.getAttribute('data-name')){
                            case "find":
                                fn("这是发现音乐。");
                                break;
                            case "mine":
                                fn("这是我的音乐。");
                                break;
                            case "friend":
                                fn("这是我的朋友。");
                                break;
                        }
                    });
                }
            }

            function curryingBindContent(content) {
                return function(text) {
                    document.querySelector(content).innerHTML = text;
                };
            }

            bindNav(curryingBindContent("#content"));
        };
       2. 延迟计算/执行        小案例:统计某地一周售楼的数量      
             /**
               * [curryingSales 创建可以获取和保存售楼数闭包函数]
               * @param {Function} fn [fn函数决定是获取周总数]
               * @return {[type]} [闭包函数]
             */
             function curryingSales(fn){
                 var dailySales=[];
                 return function(val){
                     if(val==undefined){
                          return fn.apply(null,dailySales);
                     }else{
                         dailySales.push(val);
                     }
                 };
              }
             var weeklyTotal=curryingSales(function(){
                 var sum=0;
                 for(var i=0;l=arguments.length,i<l;i++){
                     sum+=arguments[i];
                  }
                 return sum;
              });
              weeklyTotal(2);
              weeklyTotal(1);
              weeklyTotal(2);
              weeklyTotal(3);
              weeklyTotal(1);
              weeklyTotal(5);
              weeklyTotal(4);
             console.log(weeklyTotal());
       3. 提前返回          小案例:对addEventListener和attachEvent的兼容代码
  var addEvent = function() { // 浏览器支持addEventListener方法
             if(window.addEventListener){
                  return function(element, type, callback, captrue) {
                      elem.addEventListener(type, callback, captrue);
                   };
             } else { // 浏览器支持attachEvent方法
                 return function(element, type, callback) {
                      elem.attachEvent('on' + type, callback);
                      };
                 }
         }();
  二、严格模式的相关知识点      1. 出现严格模式的目的         (1)消除JavaScript语法的一些不合理、不严谨之处,减少一些怪异行为;         (2)消除代码运行的一些不安全之处,保证代码运行的安全;         (3)提高编译效率,增加运行效率;         (4)为未来新版本的JavaScript做好铺垫。      2.  严格模式标记         直接在js代码中直接添加 "use strict";即可,    在老版本浏览器会将其当做一行普通字符串忽略。      3.  使用模式          
<script>
  "use strict";
  console.log("这是严格模式");
</script>
<script>
  console.log("这是正常模式");
    "use strict";
</script>
         (2)针对单个函数                将"use strict;"放在函数体的第一行,则整个函数以"严格模式"运行。      
function strict() {
    "use strict";
    console.log("这是严格模式");
}

function noStrict() {
    console.log("这是正常模式");
    "use strict";
}
         
(function() {
    "use strict";
    // 代码块
}());
        4. 语法以及行为变化              严格模式下,对JavaScript的语法和行为,都做一些变化。        (1)全局变量的隐式声明              在正常模式中,如果一个变量没有声明就赋值,默认是全局变量。但是在严格模式已禁止这种用法,全局变量必须显式声明。             "use strict";              v = 1;             // 报错,v is not defined。
           在严格模式下,变量必须先使用var定义,再赋值。         (2)静态绑定             JavaScript语言一个特点,就是允许"动态绑定",即某些属性和方法属于哪一个对象,不是在编译时确定的,而是在运行时确定的。             严格模式对动态绑定做一些限制。某些情况下,只允许静态绑定。也就是说,属性和方法到底归属哪个对象,在编译阶段就确定。这样做有利于编译效率的提高,也使代码更易阅读,更少出现bug。             a.  禁止使用with语句               原因:with语句无法在编译阶段就确定属性到底归属哪个对象。
"use strict";
var a = 1;
// 报错,语法异常
with(obj){
    a = 2;
}
             b. eval作用域                正常模式,JavaScript语言具有两种变量作用域:全局作用域 和 函数作用域(局部作用域)。
                严格模式,具有第三种作用域:eval作用域。                正常模式下,eval语句的作用域取决于 它处于全局作用域,还是函数作用域。
                严格模式下,eval语句本身就是一个作用域,不再能够产生全局变量,其所生产的变量只能用于eval内部。              
<script>
   "use strict";
    var x = 2;
    console.log(eval("var x = 3; x"));    // 3
    console.log(x);    // 2
</script>
            c.  增强安全性              在普通函数执行模式下,禁止this关键字指向全局对象. 因此,在使用构造函数时,忘记写new,this就不再指向window对象,而是报错。就不会意外给window对象添加属性或方法。         
function foo() {
    console.log(this);     // window
}

function foo() {
    "use strict";  
    console.log(this);        // undefined
}

function foo() {
    "use strict";
    this.name = "tom";
}

var f = foo();         // 报错
             d.  禁止在函数内部访问caller以及arguments
function fn() {
    "use strict";
    fn.caller;      // 报错
    fn.arguments; // 报错
}
fn();
             e.  禁止删除变量                在严格模式下无法删除变量。只有configurable设置为true的对象属性,才能被删除。      
"use strict";
 var x;
 delete x;         // 报错
 var obj = Object.create(null, {
    "x": {
        value: 10,
        configurable: true
    }
 });
 delete obj.x;           // success
            f.  显式报错                (1) 在正常模式下,为一个对象的只读属性进行赋值,不会报错,只会默默的失败;而严格模式下,会抛出异常。
"use strict";
 var obj = {};
 Object.defineProperty(o, "a", { value: 1, writable: false });
 obj.a = 2;        // 报错
               (2) 严格模式下,对一个使用getter方法读取的属性进行赋值,会报错。
 "use strict";
  var obj = {
    get a() { return 1; }
  };
  obj.a = 2;       // 报错
               (3) 严格模式下,对禁止扩展的对象添加新属性,会报错。
"use strict";
 var obj = {};
 Object.preventExtensions(obj);
 obj.a = 1;        // 报错
               (4)严格模式下,删除一个不可删除的属性,会报错。   
"use strict";
 delete Object.prototype;         // 报错
             g. 重名错误: 严格模式新增了一些语法错误。                (1)对象不能有重名的属性                    正常模式下,如果对象有多个重名属性,最后赋值的那个属性会覆盖前面的值。严格模式下,这属于语法错误。
"use strict";
 var obj = {
    p: 1,
    p: 2
 };         // 语法错误
                (2)函数不能有重名的参数                     正常模式下,如果函数有多个重名的参数,可以用arguments[i]读取。严格模式下,这属于语法错误。                     
"use strict";
 function f(a, a, b) {        // 语法错误
    return;
}
              (3)arguments对象的限制                arguments是函数的参数对象,严格模式对它的使用做了限制。                a. 不允许对arguments赋值                  
"use strict";
 arguments++;      // 语法错误
 var obj = { set p(arguments) { } };   // 语法错误
 try { }
 catch (arguments) { }     // 语法错误
 function arguments() { }   // 语法错误
 var f = new Function("arguments", "'use strict'; return 17;");     // 语法错误
              b. arguments不再追踪参数的变化 
function f(a) {
    a = 2;
    return [a, arguments[0]];
}
f(1);         // 正常模式为[2,2]
function f(a) {
    "use strict";
    a = 2;
    return [a, arguments[0]];
}
f(1);         // 严格模式为[2,1]
              c. 禁止使用arguments.callee                  这意味着,无法在匿名函数内部调用自身了。        
"use strict";
 var f = function() { return arguments.callee; };
 f();              // 报错
          (4)函数必须声明在顶层              将来Javascript的新版本会引入"块级作用域"。为了与新版本接轨,严格模式只允许在全局作用域或函数作用域的顶层声明函数。也就是说,不允许在非函数的代码块内声明函数。
"use strict";
if (true) {
    function f1() { }       // 语法错误
}
for (var i = 0; i < 5; i++) {
    function f2() { }      // 语法错误
}
          (5)保留字              为了向将来Javascript的新版本过渡,严格模式新增了一些保留字:implements, interface, let, package, private, protected, public, static, yield。使用这些词作为变量名将会报错。           
function package(protected) {      // 出现保留字,语法错误
    "use strict";
    var implements;      //出现保留字, 语法错误
}
          此外,ECMAscript第五版本身还规定了另一些保留字(class, enum, export, extends, import, super),以及各大浏览器自行增加的const保留字,也是不能作为变量名的。   三、ES5数组的新方法      1. forEach      forEach是Array新方法中最基本的一个,就是遍历,循环。Array在ES5新增的方法中,参数都是function类型,默认有传参,forEach方法中的function回调支持3个参数,第1个是遍历的数组内容;第2个是对应的数组索引,第3个是数组本身。         
[].forEach(function(value, index, array) {
    // ...
});
     2. map         这里的map不是“地图”的意思,而是指“映射”。[].map(); 基本用法跟forEach方法类似:array.map(callback,[ thisObject]);callback参数与forEach类似:
[].map(function(value, index, array) {
    // ...
});
     案例:数组项求平方     
var data = [1, 2, 3, 4];

var squares = data.map(function (item) {
   return item * item;
});

alert(squares);       // 1, 4, 9, 16
     3. filter 过滤器       filter为“过滤”、“筛选”的意思。指数组filter后,返回过滤后的新数组。用法跟map极为相似:array.filter(callback,[ thisObject]);       filtercallback函数需要返回布尔值truefalse.    4. some和every       some:指“某些”的意思,指是否“某些项”合乎条件。every:表示是否“每一项”都要靠谱.用法如下:         array.some(callback,[ thisObject]);         简单演示:
var scores = [5, 8, 3, 10];
var current = 7;

function higherThanCurrent(score) {
  return score > current;
}

if (scores.some(higherThanCurrent)) {
  alert("朕准了!");
}
        array.every(callback,[ thisObject]);        简单演示:
var scores = [5, 8, 3, 10];
var current = 7;
if (scores.every(higherThanCurrent)) {
  console.log("朕准了!");
} else {
  console.log("来人,拖出去斩了!");        
}
      5. indexOf          indexOf方法在字符串中自古就有,string.indexOf(searchString, position)。数组的indexOf方法与之类似。用法:array.indexOf(searchElement[, fromIndex]); 返回整数索引值,如果没有匹配(严格匹配),返回-1fromIndex可选,表示从这个位置开始搜索,若缺省或格式不合要求,使用默认值0.        小案例:
var data = [2, 5, 7, 3, 5];

console.log(data.indexOf(5, "x"));      // 1 ("x"被忽略)
console.log(data.indexOf(5, "3"));     // 4 (从3号位开始搜索)

console.log(data.indexOf(4));          // -1 (未找到)
console.log(data.indexOf("5"));       // -1 (未找到,因为5 !== "5")
     6. lastIndexOf         lastIndexOf方法与indexOf方法类似:array.lastIndexOf(searchElement[, fromIndex]);只是lastIndexOf是从字符串的末尾开始查找,而不是从开头。还有一个不同就是fromIndex的默认值是array.length - 1而不是0.         
var data = [2, 5, 7, 3, 5];

console.log(data.lastIndexOf(5));      // 4
console.log(data.lastIndexOf(5, 3));    // 1 (从后往前,索引值小于3的开始搜索)

console.log(data.lastIndexOf(4));     // -1 (未找到)
      7. reduce       迭代”、“递归(recursion)”的含义,用法:array.reduce(callback[, initialValue]);callback函数接受4个参数:之前值、当前值、索引值以及数组本身。initialValue参数可选,表示初始值。若指定,则当作最初使用的previous值;如果缺省,则使用数组的第一个元素作为previous初始值,同时current往后排一位,相比有initialValue值少一次迭代。       小案例:
var arr = [10,11,12,13,14];
var sum = arr.reduce(function(prev, current, index, arr) {
    return prev + current;
}, 0);
console.log(sum);   //60
     实现上:
// 初始设置
previous = initialValue = 10, current = 11

// 第一次迭代
previous = (10 +11) =  21, current = 12

// 第二次迭代
previous = (21+ 12) =  33, current =13

// 第三次迭代
previous = (33 + 13) =  46, current = 14

//第四次迭代
previous = (46 + 14) =  60, current = undefined(退出)
         8.reduceRight        用法与reduce相似,实现上差异在于reduceRight是从数组的末尾开始实现。        小案例:
var data = [1, 2, 3, 4];
var special = data.reduceRight(function (previous, current, index) {
  if (index == 0) {
    return previous + current;
  }
  return previous - current;
});

console.log(special);   // 0
     实现上:
// 初始设置
index = 3, previous = initialValue = 4, current = 3

// 第一次迭代
index = 2, previous = (4- 3) = 1, current = 2

// 第二次迭代
index = 1, previous = (1 - 2) = -1, current = 1

// 第三次迭代
index = 0, previous = (-1 + 1) = 0, current = undefined (退出)
   四、创建对象的方式        1. 工厂模式            创建对象,返回带有属性和方法的person对象
function createPerson(name, age,gender)
{
    var person = new Person();
    person.name=name;
    person.age=age;
    person.gender=gender
    person.sayName=function()
    {
        alert(this.name);
    };
    return person;
}
createPerson("tom",20,"男").sayName();
        2. 构造函数模式          创建对象,这种方式有个缺陷是sayName这个方法,它的每个实例都是指向不同的函数实例,而不是同一个。
function Person(name,age,gender)
{
    this.name=name;
    this.age=age;
    this.gender=gender;
    this.sayName=function()
    {
        console.log(this.name);
    };
}

var person = new Person("rose",23,"女");
person.sayName();
        3. 原型模式           创建对象,解决了上述中提到的缺陷,使不同的对象的函数(如sayFriends)指向了同一个函数。但它本身也有缺陷,就是实例共享了引用类型friends,从下面的代码执行结果可以看到,两个实例的friends的值是一样的。          
function Person()
{
}
Person.prototype = {
    constructor : Person,
    name:"Tom",
    age:20,
    gender:"男",
    friends:["Jack","Rose"],
    sayFriends:function()
    {
        console.log(this.friends);
    }
};
var person1 = new Person();
person1.friends.push("Mary");
person1.sayFriends();   //Jack,Rose,Mary
var person2 = new Person(); 
person2.sayFriends();   //Jack,Rose,Mary
        4. 组合模式(推荐)            使用原型模式和构造函数创建对象,解决了上述方法中提到的缺陷,而且这也是使用最广泛、认同度最高的创建对象的方法,强烈推荐使用。
function Person(name,age,gender)
{
    this.name=name;
    this.age=age;
    this.gender=gender;
   this.friends=["Jack","Rose"];
}
Person.prototype.sayFriends=function()
{
    console.log("My name is"+this.name+","+"My friends are"+this.friends);
};
var person1 = new Person("Tom",20,"男");
var person2 = new Person("Alice",18,"女");
person1.friends.push("Marck");
person1.sayFriends();  //My name is Tom ,My friends are Jack,Rose,Marck
person2.sayFriends();  //My name is Alice,My friends are Jack,Rose
         5. 动态原型          这个模式的好处在于看起来更像传统的面向对象编程,具有更好的封装性,因为在构造函数里完成了对原型创建。这也是一个推荐的创建对象的方法。
function Person(name, age) {
    this.name = name;
    this.age = age;
    // method
    if(typeof this.sayName !== 'function'){
        Person.prototype.sayName = function() {
            console.log(this.name);
        }
    }
}
        6. 寄生构造函数模式  (不常用,不推荐)      
function Person(name, age) {
    var obj = new Object;
    obj.name = name;
    obj.age = age;
    obj.sayName = function() {
        console.log(this.name);
    };
    return obj;
}
        7. 稳妥构造函数模式 (不常用,不推荐) 
function Person(name, age) {
    var obj = new Object;
    obj.sayName = function() {
        console.log(name);
    };
}
   五、继承方式       1. 原型式          利用原型让一个引用类型继承另外一个引用类型的属性和方法
function A() {
            
        }
        A.prototype.location = "地球上。";
        A.prototype.say = function() {};
        A.prototype.talk = function() {};
        A.prototype.run = function() {};
        var a = new A;
        console.log(a.location);

        A.prototype = {
            say:function() {},
            talk:function() {},
            run:function() {}
        };
        var na = new A;
        console.log(na);
    2. 混入式
var o1 = {
            name: 'tom'
        };
        var o2 = {
            age: 18
        };
        var o3 = {
            gender: '男'
        };    
        console.log(o1);
        o1.extend = function() {
            var k,
                i = 0,
                l = arguments.length,
                args = arguments;
            // 遍历在arguments上的每一对象
            for (; i < l; i++) {
                // 枚举当前对象上的所有属性,添加到this上
                for (k in args[i]) {
                    this[k] = args[i][k];
                }
            }
        };
        o1.extend(o2, o3);
        console.log(o1);
     3. 借用构造函数          在子类型构造函数的内部调用超类构造函数,通过使用call()和apply()方法可以在新创建的对象上执行构造函数。
function SuperType() {
this.colors = ["red","blue","green"];
}
function SubType() {
SuperType.call(this);//继承了SuperType
}
var instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors);//"red","blue","green","black"
var instance2 = new SubType();
console.log(instance2.colors);//"red","blue","green"
     4. 对象冒充
function parent(name, age) {
            this.name = name;
            this.age = age;
        }

        function child(name, age, gender, address) {
            // 将parent作为child对象的一个方法来调用
            this.parent = parent;
            this.parent(name, age);
            delete this.parent;
            this.gender = gender;
            this.address = address;
        }

        var ch = new child('tom', 18, 'boy', 'beijing');
        console.log(ch);
         5. 组合继承(借用构造函数、原型)            将原型链和借用构造函数的技术组合在一块,从而发挥两者之长的一种继承模式。        6. 寄生式(不常用)           创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真正是它做了所有工作一样返回对象。        7. 组合寄生(不常用)          通过借用函数来继承属性,通过原型链的混成形式来继承方法          

 

                                                                                                    

相关内容推荐