复习及今日计划:
复习:
简单的原型写法
给内置类添加原型:
以后所有的 String 都可以使用sayHi了!!!
今日计划:
原型链
继承
原型的另一个作用
this指向
函数是对象,对象不一定是函数。
原型和 原型链:
复习之前为什么使用原型:
如上代码所示,之前使用原型就是为了解决上述的问题,不然的话,每个对象中都会有一个sayHi()方法,所以可以在Person的原型中加入此方法,共享数据,节省空间。
1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Document</title> 6 </head> 7 <body> 8 <script> 9 function Person(name, age) { 10 this.name = name; 11 this.age = age; 12 } 13 Person.prototype.sayHi = function () { 14 console.log("您好,您真帅"); 15 }; 16 for (var i =0;i<100;i++){ 17 var per = new Person("tom",18); 18 per.sayHi(); 19 } 20 </script> 21 </body> 22 </html>
原型链:
这种关系是通过 原型 (实例对象中的__proto__/ Person中的prototype) 联系的
this指向:
无论是构造函数的方法中的this ,还是 原型的方法中的this 都是指的是 构造函数的对象本身。
实例对象的__proto__ /构造函数的prototype 指向 可以改变(原型的指向可以改变):
1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Document</title> 6 </head> 7 <body> 8 <script> 9 function Person(name, age) { 10 this.name = name; 11 this.age = age; 12 } 13 Person.prototype.sayHi = function () { 14 console.log("您好,您真帅"); 15 }; 16 Person.prototype = { 17 eat:function () { 18 console.log("吃饭"); 19 } 20 }; 21 var per = new Person("tom",18); 22 // per.sayHi(); //报错,是因为此时的原型指向已经改变了。此时原型中只有 eat 方法没有sayHi 方法 23 24 </script> 25 </body> 26 </html>
1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Document</title> 6 </head> 7 <body> 8 <script> 9 function Person(name, age) { 10 this.name = name; 11 this.age = age; 12 } 13 Person.prototype = { 14 eat:function () { 15 console.log("吃饭"); 16 } 17 }; 18 Person.prototype.sayHi = function () { 19 console.log("您好,您真帅"); 20 }; 21 var per = new Person("tom",18); 22 per.sayHi(); //此时,不会报错,原本的原型已经改变,再添加一个sayHi 方法,当然不会报错,此时原型中有两个方法eat 和 sayHi 23 per.eat(); 24 25 26 </script> 27 </body> 28 </html>
原型链 的最终指向:
1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Document</title> 6 </head> 7 <body> 8 <script> 9 function Person(name, age) { 10 this.name = name; 11 this.age = age; 12 } 13 Person.prototype.sayHi = function () { 14 console.log("您好,您真帅"); 15 }; 16 var per = new Person("tom",18); 17 18 console.dir(Person); 19 console.dir(per); 20 21 </script> 22 </body> 23 </html>
上面代码中,per 是Person的对象 ,per中的 __proto__ 指向的是Person.prototype。
可,Person.prototype也是对象,它里面也应该有__proto__。它指向谁呢?
对于一个对象,我们可以通过输出 对象.__proto__ 来看它的指向!
例如:
1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Document</title> 6 </head> 7 <body> 8 <script> 9 function Person(name, age) { 10 this.name = name; 11 this.age = age; 12 } 13 Person.prototype.sayHi = function () { 14 console.log("您好,您真帅"); 15 }; 16 var per = new Person("tom",18); 17 18 //per 的指向 19 console.log(per.__proto__); //Person 的prototype 20 21 22 23 24 </script> 25 </body> 26 </html>
1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Document</title> 6 </head> 7 <body> 8 <script> 9 function Person(name, age) { 10 this.name = name; 11 this.age = age; 12 } 13 Person.prototype.sayHi = function () { 14 console.log("您好,您真帅"); 15 }; 16 var per = new Person("tom",18); 17 18 //per 的指向 19 console.log(per.__proto__); //Person 的prototype 20 21 //Person.prototype 也是个对象 22 console.log(Person.prototype.__proto__); 23 //可以发现 它的指向是 Object 的prototype !!! 24 25 </script> 26 </body> 27 </html>
1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Document</title> 6 </head> 7 <body> 8 <script> 9 function Person(name, age) { 10 this.name = name; 11 this.age = age; 12 } 13 Person.prototype.sayHi = function () { 14 console.log("您好,您真帅"); 15 }; 16 var per = new Person("tom",18); 17 18 //per 的指向 19 console.log(per.__proto__); //Person 的prototype 20 21 //Person.prototype 也是个对象 22 console.log(Person.prototype.__proto__); 23 //可以发现 它的指向是 Object 的prototype !!! 24 25 //Object.prototype 也是个对象 26 console.log(Object.prototype.__proto__); 27 //可以发现 它的指向是 null 28 29 30 </script> 31 </body> 32 </html>
1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Document</title> 6 </head> 7 <body> 8 <script> 9 function Person(name, age) { 10 this.name = name; 11 this.age = age; 12 } 13 Person.prototype.sayHi = function () { 14 console.log("您好,您真帅"); 15 }; 16 var per = new Person("tom",18); 17 18 //per 的指向 19 // console.log(per.__proto__); //Person 的prototype 20 21 //Person.prototype 也是个对象 22 // console.log(Person.prototype.__proto__);//Object 的prototype 23 //可以发现 它的指向是 Object 的prototype !!! 24 25 //Object.prototype 也是个对象 26 // console.log( [Person.prototype.__proto__](Object.prototype) .__proto__); 27 28 29 //验证!!! 30 //按照理论 下面二者应该都是 null 31 console.log(Person.prototype.__proto__.__proto__); 32 console.log(Object.prototype.__proto__); 33 34 35 36 </script> 37 </body> 38 </html>
总结:
例子:var per = new Person();
per 的原型指向的是 Person.prototype .
Person.prototype的原型指向是 Object.prototype.
Object.prototype 的原型指向是 null.
一般先改变原型指向再添加方法:
原型指向改变之后,它之前的所有方法都是访问不到的。所以一般都是先改变原型指向,然后再添加方法!
注 :改变原型的方法,一般如下:
Person.prototype = {} ; 改变了原型指向
Person.prototype.name = " "; 未改变原型指向 !
实例对象 和 原型对象属性 重名问题:
例子:var per = new Person();
问题是:per 的属性 和 Person.prototype 的属性的重名问题!
如果console.log(per.name);
它是一层一层的搜索,在实例对象中能找到的。就不会去 原型中找了。
如果就是改变原型中的属性的值,怎么办?
不能通过实例对象 直接修改,应该通过 原型对象修改。
即不能使用per.name =" "; 应该是 Person.prototype.name = " "; 修改!
另外:
1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Document</title> 6 </head> 7 <body> 8 <script> 9 function Person(name, age) { 10 this.name = name; 11 this.age = age; 12 } 13 Person.prototype.sayHi = function () { 14 console.log("您好,您真帅"); 15 }; 16 var per = new Person("tom",18); 17 18 console.log(per.aaajdkfj); //不会报错,结果是 undefined 19 console.log(aaajdkfj); //报错,因为不是对象直接点! 20 21 22 23 </script> 24 </body> 25 </html>
一个神奇的原型链:
回顾:
什么是原型链:实例对象 和 原型对象的关系,它是通过__proto__来联系的!
1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Document</title> 6 </head> 7 <body> 8 <div id="dv"></div> 9 <script> 10 var divObj = document.getElementById("dv"); 11 console.dir(divObj); 12 </script> 13 </body> 14 </html>
总结:
查看对象的原型指向 ,就是通过输出 console.dir(对象.__proto__)
继承:
变原型指向 实现继承:
继承也是一种关系,它指的是类与类之间的关系!
1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Document</title> 6 </head> 7 <body> 8 <div id="dv"></div> 9 <script> 10 function Person(name, age) { 11 this.name = name; 12 this.age = age; 13 } 14 Person.prototype.eat = function () { 15 console.log("吃饭 "); 16 }; 17 Person.prototype.sleep = function () { 18 console.log("睡觉"); 19 }; 20 21 function Student(name, age) { 22 this.name = name; 23 this.age = age; 24 } 25 Student.prototype.eat = function () { 26 console.log("吃饭"); 27 }; 28 Student.prototype.sleep = function () { 29 console.log("睡觉"); 30 }; 31 Student.prototype.study = function () { 32 console.log("学习"); 33 }; 34 35 </script> 36 </body> 37 </html>
1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Document</title> 6 </head> 7 <body> 8 <div id="dv"></div> 9 <script> 10 function Person(name, age) { 11 this.name = name; 12 this.age = age; 13 } 14 Person.prototype.eat = function () { 15 console.log("吃饭 "); 16 }; 17 Person.prototype.sleep = function () { 18 console.log("睡觉"); 19 }; 20 21 function Student() { 22 } 23 24 Student.prototype = new Person("tom",18); //Student 通过 改变原型指向 继承 Person! 25 Student.prototype.study = function () { 26 console.log("学习"); 27 }; 28 29 var stu = new Student(); 30 stu.eat(); 31 stu.sleep(); 32 stu.study(); 33 34 35 36 37 </script> 38 </body> 39 </html>
继承 ---原型链的图:
借用构造函数 继承:
1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Document</title> 6 </head> 7 <body> 8 <div id="dv"></div> 9 <script> 10 function Person(name, age) { 11 this.name = name; 12 this.age = age; 13 } 14 Person.prototype.eat = function () { 15 console.log("吃饭 "); 16 }; 17 Person.prototype.sleep = function () { 18 console.log("睡觉"); 19 }; 20 21 function Student(score) { 22 this.score = score; 23 } 24 Student.prototype = new Person("tom",18); //继承 ! 25 Student.prototype.study = function () { 26 console.log("学习"); 27 }; 28 29 var stu1 = new Student("100"); 30 // stu1.name = "egon"; //重新赋值! 31 var stu2 = new Student("110"); 32 var stu3 = new Student("120"); 33 console.log(stu1.name +" "+ stu1.age +" "+ stu1.score); 34 console.log(stu2.name +" "+ stu2.age +" "+ stu2.score); 35 console.log(stu3.name +" "+ stu3.age +" "+ stu3.score); 36 37 //此时的缺点: 38 //为了数据共享,通过 改变原型指向 做到了继承。 39 // 但是,此时因为改变原型指向 直接初始化了属性,所以,继承过来的属性都是一样的。 40 41 //这个时候,只能通过重新调用 对象的属性 进行重新赋值! 但是这样不好,麻烦! 42 43 </script> 44 </body> 45 </html>
1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Document</title> 6 </head> 7 <body> 8 <div id="dv"></div> 9 <script> 10 function Person(name, age) { 11 this.name = name; 12 this.age = age; 13 } 14 Person.prototype.eat = function () { 15 console.log("吃饭 "); 16 }; 17 Person.prototype.sleep = function () { 18 console.log("睡觉"); 19 }; 20 21 function Student(name,age,score) { 22 Person.call(this,name,age); 23 this.score = score; 24 } 25 Student.prototype.study = function () { 26 console.log("学习"); 27 }; 28 29 var stu1 = new Student("tom",18,100); 30 var stu2 = new Student("egon",28,190); 31 var stu3 = new Student("alex",34,10); 32 console.log(stu1.name +" "+ stu1.age +" "+ stu1.score); 33 console.log(stu2.name +" "+ stu2.age +" "+ stu2.score); 34 console.log(stu3.name +" "+ stu3.age +" "+ stu3.score); 35 36 37 38 39 40 </script> 41 </body> 42 </html>
下面的组合继承可以解决这个问题----父类方法不能被继承过来!
使用组合继承:
所谓组合继承,指的是 之前说过的两种继承方式 的组合:
1,通过改变原型指向 实现继承
2,借用父类构造函数 实现继承
1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Document</title> 6 </head> 7 <body> 8 <div id="dv"></div> 9 <script> 10 function Person(name, age) { 11 this.name = name; 12 this.age = age; 13 } 14 Person.prototype.eat = function () { 15 console.log("吃饭 "); 16 }; 17 Person.prototype.sleep = function () { 18 console.log("睡觉"); 19 }; 20 21 function Student(name,age,score) { 22 Person.call(this,name,age); //1,借用父类 构造函数 实现继承 23 this.score = score; 24 } 25 Student.prototype = new Person(); //2,通过 修改原型指向 实现继承 //不用再初始化了,因为里面使用了call 来初始化! 26 Student.prototype.study = function () { 27 console.log("学习"); 28 }; 29 30 var stu1 = new Student("tom",18,100); 31 var stu2 = new Student("egon",28,190); 32 var stu3 = new Student("alex",34,10); 33 console.log(stu1.name +" "+ stu1.age +" "+ stu1.score); 34 console.log(stu2.name +" "+ stu2.age +" "+ stu2.score); 35 console.log(stu3.name +" "+ stu3.age +" "+ stu3.score); 36 37 stu1.eat(); 38 stu1.sleep(); 39 stu1.study(); 40 41 42 43 44 </script> 45 </body> 46 </html>
拷贝继承:
但是,对于var obj2 = obj1; //它仅仅是改变了地址的指向!
第二三种 的拷贝方式如下:
但是,第二三种拷贝仍然是 浅拷贝,只是仅仅将一个空间中的内容完全的复制到另一块空间上!
后面会有深拷贝,它会将所有能找到的地址内容都拷贝,然后不停的申请新内存存储!是真实存储了数据的方式!
总结:
第一种:直接用= 号的,它其实就是一个内存多了一个引用。
第二种,第三种 (浅拷贝):是多了一份空间。
深拷贝:可能会多很多空间。因为可能会不停的拷贝!
继承 总结:
再看原型链 图:
1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Document</title> 6 </head> 7 <body> 8 <div id="dv"></div> 9 <script> 10 function F1(age) { 11 this.age = age; 12 } 13 function F2(age) { 14 this.age = age; 15 } 16 function F3(age) { 17 this.age =age; 18 } 19 var f1 = new F1(10); 20 var f2 = new F2(20); 21 var f3 = new F3(30); 22 </script> 23 </body> 24 </html>
上面代码的 原型链图如下:
1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Document</title> 6 </head> 7 <body> 8 <div id="dv"></div> 9 <script> 10 function F1(age) { 11 this.age = age; 12 } 13 F2.prototype = new F1(10); 14 function F2(age) { 15 this.age = age; 16 } 17 F3.prototype = new F2(20); 18 function F3(age) { 19 this.age =age; 20 } 21 var f3 = new F3(30); 22 console.log(f3.age); //应该是 30 23 24 </script> 25 </body> 26 </html>
上面代码的原型链图:
函数:
函数的角色:
1,函数的声明
2,函数表达式
函数声明和 函数表达式的区别:
所以,以后写函数,尽量都写 var func = funciton () {};
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>title</title> 6 <script> 7 8 //函数声明 IE 8 会出错 ! 9 // 10 // if(true){ 11 // function f1() { 12 // console.log("哈哈,我又变帅了"); 13 // } 14 // }else{ 15 // function f1() { 16 // console.log("小苏好猥琐"); 17 // } 18 // } 19 // f1(); 20 21 22 //函数表达式 不会报错! 23 24 var ff; 25 if(true){ 26 ff=function () { 27 console.log("哈哈,我又变帅了"); 28 }; 29 }else{ 30 ff=function () { 31 console.log("小苏好猥琐"); 32 }; 33 } 34 ff(); 35 36 //函数声明如果放在if-else的语句中,在IE8的浏览器中会出现问题 37 //以后宁愿用函数表达式,都不用函数声明 38 39 40 41 </script> 42 </head> 43 <body> 44 45 46 </body> 47 </html>
this大汇总:
第一个问题:
对于第一个,我们知道 BOM中的一个顶级对象是 window 。浏览器中所有的东西都是window的。
因此,其实f1(); 它的完整是 window.f1();
所以,普通函数中的this是window !
第二个问题:
对象.方法中的this指的是 实例对象
第三个问题:
和第一个一样,定时器中的this 也是window !
setInterval() 其实完整写法是:window.setInterval();
第四个问题:
构造函数中的this 是 实例对象
第五个问题:
原型对象方法中的this指的是 实例对象
但是,上述的代码都是在非严格模式下的情况,
如果在代码上面写上了 “use strict”; //就和上面有所不同了
函数的不同调用方式:
函数也是对象,对象不一定是 函数:
对象的里面都有__proto__
构造函数中都有prototype
所以,
有__proto__的就是对象
有prototype 的就是(构造)函数
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>这是网页标题</title> 6 7 </head> 8 <body> 9 <script> 10 function f1() { 11 12 } 13 function F1() { 14 15 } 16 console.dir(f1); 17 console.dir(F1); 18 19 </script> 20 </body> 21 </html>
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>这是网页标题</title> 6 7 </head> 8 <body> 9 <script> 10 11 console.dir(Math); //因为Math 中有 __proto__故,Math 是对象 12 //因为Math中没有prototype,所以Math 不是(构造)函数 13 14 </script> 15 </body> 16 </html>
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>这是网页标题</title> 6 7 </head> 8 <body> 9 <script> 10 11 function f1() { 12 13 } 14 console.dir(f1.prototype); 15 //其实所有的 构造函数的原型对象 都不是函数,只是个对象!!! 16 17 </script> 18 </body> 19 </html>
那么,既然函数是对象,它是有谁创建出来的呢?
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>这是网页标题</title> 6 7 </head> 8 <body> 9 <script> 10 11 function f1() { 12 13 } 14 console.dir(f1); 15 16 </script> 17 </body> 18 </html>
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>这是网页标题</title> 6 7 </head> 8 <body> 9 <script> 10 // function f1(num1, num2) { 11 // return num1 + num2; 12 // } 13 // console.log(f1(10,20)); 14 //上述代码可以这样写 15 16 var f1 = new Function("num1","num2","return num1 + num2"); 17 console.log(f1(10,20)); 18 19 20 </script> 21 </body> 22 </html>
前面的内容不全! 现在加上Function !
之前没有考虑到 (构造)函数 也是个对象!现在加上,它也是个对象,它的构造函数是Function!
它是所有(构造)函数 的构造函数,
它的构造函数是Object。
数组中函数的调用:
我们知道数组中可以存储任何类型的数据!
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>这是网页标题</title> 6 7 </head> 8 <body> 9 <script> 10 11 function f1() { 12 13 } 14 console.log(typeof f1); //Function 15 16 </script> 17 </body> 18 </html>
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>title</title> 6 <script> 7 8 //数组可以存储任何类型的数据 9 var arr=[ 10 function () { 11 console.log("十一假期快乐"); 12 }, 13 function () { 14 console.log("十一假期开心"); 15 } 16 , 17 function () { 18 console.log("十一假期健康"); 19 } 20 , 21 function () { 22 console.log("十一假期安全"); 23 }, 24 function () { 25 console.log("十一假期如意"); 26 } 27 ]; 28 //回调函数:函数作为参数使用 29 arr.forEach(function (ele) { 30 ele(); 31 }); 32 33 </script> 34 </head> 35 <body> 36 37 38 </body> 39 </html>
回顾:Array 的方法 .forEach 的使用:
forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void;
/**
* Calls a defined callback function on each element of an array, and returns an array that contains the results.
* @param callbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.
* @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
*/
它需要一个函数作为参数! 该函数有三个传入参数!
数组会传递给 该函数三个参数!
第1个是遍历的数组内容;第2个是对应的数组索引,第3个是数组本身
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>title</title> 6 <script> 7 arr = [1,2,3]; 8 9 function func(){ //argu 是个传入参数, 10 console.log(arguments.length); //3 说明 arr 传递了3个参数过来! 打印了3遍 11 } 12 arr.forEach(func); //arr 会给 func中的参数赋值 ! 13 14 </script> 15 </head> 16 <body> 17 18 19 </body> 20 </html>
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>title</title> 6 <script> 7 arr = [1,2,3]; 8 9 //使用.forEach 遍历 arr 10 arr.forEach(function (ele) { 11 console.log(ele); 12 }) 13 14 </script> 15 </head> 16 <body> 17 18 19 </body> 20 </html>
注:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>title</title> 6 <script> 7 var arr = [ 8 function f() { 9 console.log("哈哈哈"); 10 }, 11 function () { 12 console.log("ahhahahh"); 13 }, 14 function () { 15 console.log("wowowoowo"); 16 } 17 ] 18 arr.forEach(function (ele) { 19 ele(); 20 }) 21 </script> 22 </head> 23 <body> 24 25 26 </body> 27 </html>