一、深入原型

  • 普通函数默认返回的是undefined; return undefined
  • 构造函数在实例化对象的时候,系统默认返回的是this; return this
  • 原型的原型一定是由Object这个基类所构造出来的
  • 不是所有的对象都继承与Object.prototype,如果是基于Object.create(null)创建出来的对象,它是没有原型的;是一个纯粹的空对象
  • null和undefined都是不能调用toString方法的,因为两个都是属于原始值,原始值是没有属性或者是方法的,且无法进行包装类,有没有原型;所以说他们不能继承Object.prototype上的toString方法
  • 系统内置的包装类上都有toString方法,作用是将其装换为字符串,包装类是将这个方法重写了的;Object.prototype上也有toString方法,不过这个是用于数据类型检测的
  • proto:适用于保存原型的容器
  • 原型的最顶端是Object.prototype
  • 原型链:沿着prototype这条线去寻找原型上的属性或者方法,一层一层的去继承;这个链条就叫做原型链
  • 原型链上的属性或者是方法,想要增删改,只能是它自己本身;后代是无法增删改其原型链上的属性或者是方法的
  • document.write打印出来的是是字符串的格式,alert输出的也是字符串格式

    1. // 深入原型和原型链
    2. // 普通函数默认返回的是undefined; return undefined
    3. // 构造函数在实例化对象的时候,系统默认返回的是this; return this
    4. // 原型的原型一定是由Object这个基类所构造出来的
    5. // 不是所有的对象都继承与Object.prototype,如果是基于Object.create(null)创建出来的对象,它是没有原型的;是一个纯粹的空对象
    6. // null和undefined都是不能调用toString方法的,因为两个都是属于原始值,原始值是没有属性或者是方法的,且无法进行包装类,有没有原型;所以说他们不能继承Object.prototype上的toString方法
    7. // 系统内置的包装类上都有toString方法,作用是将其装换为字符串,包装类是将这个方法重写了的;Object.prototype上也有toString方法,不过这个是用于数据类型检测的
    8. // __proto__:适用于保存原型的容器
    9. // 原型的最顶端是Object.prototype
    10. // 原型链:沿着__prototype__这条线去寻找原型上的属性或者方法,一层一层的去继承;这个链条就叫做原型链
    11. // 原型链上的属性或者是方法,想要增删改,只能是它自己本身;后代是无法增删改其原型链上的属性或者是方法的
    12. // document.write打印出来的是是字符串的格式,alert输出的也是字符串格式
    13. function Car(){}
    14. var car = new Car();
    15. console.log(car);
    16. console.log(Car.prototype);

    image.png

    二、原型链和原型继承

    2.1、JS中的原型继承和原型链

  • 原型本身也有原型

  • proto是用来保存原型的一个容器
  • 原型链:沿着proto去寻找原型上的属性或者是方法,一层一层的去继承,这个链条就叫做原型链
  • 原型链的最顶端是Object.prototype

    1. // 原型链:沿着__proto__去寻找原型上的属性或者是方法,一层一层的去继承,这个链条就叫做原型链
    2. // 原型继承
    3. Professor.prototype.tSkill = 'JAVA';
    4. function Professor(){}
    5. var professor = new Professor;
    6. Teacher.prototype = professor;
    7. function Teacher(){
    8. this.mSkill = 'JS/JQ';
    9. }
    10. var teacher = new Teacher();
    11. Student.prototype = teacher;
    12. function Student(){
    13. this.pSkill = 'HTML/CSS';
    14. }
    15. var student = new Student();
    16. console.log(student);
    17. console.log(student.mSkill);
    18. console.log(student.tSkill);

    image.png

    2.2、原型链上的属性或方法是否能够修改

  • Object.prototype上有一个toString方法,改变其this指向,可以用来检测数据类型

  • 原型链上的属性或者是方法想要实现增删改,原则上只能是自己去做,后代原则上是无法修改原型链上的属性和方法的
  • 原型链上的属性如果是原始值,则后代无法去修改;但是要是该属性为引用类型,则可以修改
  • 如果原型链上的属性是引用类型的,那么后代是可以增删改原型链上的属性或者是方法的,但是我们并不建议这么去做

    1. var obj = {
    2. age: 18
    3. }
    4. obj.name = 'kola';
    5. console.log(obj);
  • 原型链上的属性如果是原始值,则后代无法去修改;但是要是该属性为引用类型,则可以修改

    1. // 型链上的属性如果是原始值,则后代无法去修改;但是要是该属性为引用类型,则可以修改
    2. // 如果原型链上的属性是引用类型的,那么后代是可以增删改原型链上的属性或者是方法的,但是我们并不建议这么去做
    3. Professor.prototype.mSkill = 'JAVA';
    4. function Professor(){}
    5. var professor = new Professor();
    6. Teacher.prototype = professor;
    7. function Teacher(){
    8. this.mSkill = 'JS/JQ';
    9. this.scuccess = {
    10. alibaba: 28,
    11. tencent: 10
    12. }
    13. }
    14. var teacher = new Teacher();
    15. Student.prototype = teacher;
    16. function Student(){
    17. this.pSkill = 'HTML/CSS';
    18. }
    19. var student = new Student();
    20. student.scuccess.baidu = 30;
    21. student.scuccess.alibaba = 90;
    22. console.log(teacher);
    23. console.log(student);

    image.png

    1. // 如果原型上的属性是原始值,那么后代是不可以修改原型上的属性的
    2. Professor.prototype.mSkill = 'JAVA';
    3. function Professor(){}
    4. var professor = new Professor();
    5. Teacher.prototype = professor;
    6. function Teacher(){
    7. this.mSkill = 'JS/JQ';
    8. this.students= 500;
    9. }
    10. var teacher = new Teacher();
    11. Student.prototype = teacher;
    12. function Student(){
    13. this.pSkill = 'HTML/CSS';
    14. }
    15. var student = new Student();
    16. student.students++;
    17. // student.students++ => student.students = student.students+1
    18. // student = {
    19. // students:
    20. // }
    21. console.log(teacher);
    22. console.log(student);

    image.png

    三、this指向问题

  • this指向:普通函数执行,看前面有没有点,点前面是谁this就指向谁

  • 谁用this就指向谁

    1. // this指向问题:普通函数执行,看前面有没有点,有点,点前面是谁this就指向谁
    2. function Car(){
    3. this.brand = 'Benz'
    4. }
    5. Car.prototype = {
    6. brand: 'Mazda',
    7. instro: function(){
    8. console.log('我是' + this.brand + '车');
    9. }
    10. }
    11. var car = new Car();
    12. car.instro();
    13. // 谁用this就指向谁
    14. Car.prototype.instro();
    15. console.log(Car.prototype.brand);

    四、构造函数实例化后默认返回this

  • 构造函数在实例化对象后默认返回的是this「系统默认返回的」;return this

  • 普通函数默认返回的是undefined; return undefined

    1. // 构造函数在实例化对象后默认返回的是this「系统默认返回的」;return this
    2. // 普通函数默认返回的是undefined; return undefined
    3. function Person(){
    4. this.somke = function(){
    5. this.weight--;
    6. }
    7. }
    8. Person.prototype = {
    9. weight: 130
    10. }
    11. var person = new Person();
    12. person.somke();
    13. console.log(Person.prototype);
    14. console.log(Person);

    五、声明对象的两种方式

  • 字面量

  • 系统自带的构造函数方式

    1. // 声明对象的两种方式
    2. // 字面量方式
    3. // 系统自带的构造函数方式
    4. // 两者声明出的对象没有什么区别
    5. var obj1 = {};
    6. var obj2 = new Object(); // 公司不用这种
    7. console.log(obj1, obj2);
    8. // 通过构造函数实例化对象
    9. function Obj(){}
    10. var obj3 = new Obj();
    11. console.log(obj3);

    image.png

    六、原型的原型一定指向系统构造函数

    1. // 原型的原型一定指向系统自带的构造函数
    2. function Obj(){}
    3. var obj = new Obj();
    4. console.log(Obj.__proto__); // ƒ () { [native code] }

    七、Object.create(原型对象,null)

  • Object.create(原型对象);创建一个空对象,可以自己定义原型,不用使用系统自带的原型

  • Object.create(null);可以创建一个纯粹的空对象,空对象里面什么属性也没有

    1. // Object.create(原型对象); 创建一个空对象,可以自己定义原型,不用使用系统自带的原型
    2. // Object.create(null); 创建一个纯粹的空对象,对象里什么属性都没有
    3. var test = {
    4. num: 2,
    5. name: 'kola'
    6. }
    7. function Obj(){}
    8. Obj.prototype.num = 1;
    9. var obj1 = Object.create(test);
    10. var obj2 = new Obj();
    11. console.log(obj1);
    12. console.log(obj2);

    image.png

  • Object.create(null);可以创建一个纯粹的空对象,空对象里面什么属性也没有

    1. var obj1 = Object.create(null);
    2. console.log(obj1);

    image.png

    1. var obj1 = Object.create(null);
    2. console.log(obj1);
    3. obj1.num = 100;
    4. var obj2 = Object.create(obj1);
    5. console.log(obj2);
    6. console.log(obj2.num);

    image.png

    八、自己指定proto没有用,这个属性必须是系统的

  • 自己执行proto是没有用的,proto属性必须是系统自带的

  • proto必须是系统内置的,可以更改它,但是不可以自造
  • 不是所有的对象都继承与Object.prototype,比如说基于Object.create(null)所创建出来的对象

    1. // 自己指定__proto__是没有用的,proto属性必须是系统自带的
    2. var obj = Object.create(null);
    3. obj.num = 100;
    4. console.log(obj); // 这是一个没有原型的对象
    5. var obj1 = {
    6. count: 2
    7. }
    8. obj.__proto__ = obj1; // proto必须是系统内置的,可以更改它,但是不可以自造
    9. console.log(obj);

    九、null和undefined无法使用toString方法

  • null和undefined都属于原始值类型,原始值类型是没有属性和方法的

  • null和undefined又无法通过系统的包装类将其包装为对象,因为没有变量去保存
  • unll的undefined没有原型,所以无法继承原型上的toString方法

    1. // null和undefined无法调用toString方法
    2. // 因为null和undefined都是属于原始值,原始值是没有属性和方法的
    3. // null和undefined无法通过系统的包装类将其包装为象,因为没有变量去保存
    4. // unll和undefined都没有原型,所以无法继承原型上的toString方法
    5. console.log(null.toString()); // 报错
    6. console.log(undefined.toString()); // 报错

    十、系统包装类上的toString方法和Object.prototype上的toString方法的区别

  • 系统包装类原型上的toString方法的作用是将值转换为字符串

  • Object.prototype上的toString方法,通过改变其this指向,可以实现数据类型检测「万能的数据类型检测」

    1. // 系统包装类原型上的toString方法和Object.prototype上的toString方法作用是不一样的
    2. var num = 100;
    3. console.log(num.toString()); // 原始值没有属性和方法,这里是经过了系统的包装类,并且有变量保存

    原始值没有属性和方法,系统内置的包装类的原型上都有toString方法,作用是将值转换为字符串

    1. // 原始值没有属性和方法,系统内置的包装类的原型上都有toString方法,作用是将值转换为字符串
    2. var num1 = 110;
    3. var num2 = new Number(num1);
    4. console.log(num2.toString());
    5. console.log(num2);

    document.write输出的都是字符串格式,在打印的过程中存在隐式转换,会将值转换为字符串再打印出

  • Object.create(null)创建的是一个纯粹的空对象,没有原型

    1. // document.write打印的过程中存在着隐式类型转换,会将值转换为字符串格式
    2. var num = 100;
    3. var obj = {}
    4. var obj2 = Object.create(null);
    5. document.write(num); // '100'
    6. document.write(obj); // [object Object]
    7. // 首先基于Object.prototype创建出来的对象是没有原型的,它是一个纯粹的空对象
    8. // 既然没有原型,那它就不能继承原型上的toString方法,就不能将obj2转换为字符串
    9. // 然而document.write输出的结果又必须是字符串,所以就只能报错了
    10. document.write(obj2); // 报错 Cannot convert object to primitive value
  • 系统包装类的原型上都有toString方法,作用是将值转换为字符串

  • Object.prototype上也有toString方法,通过改变其this指向,可以实现数据类型检测「万能的」

    1. // 系统包装类上都有toSting方法,作用是将值转换为字符串
    2. // Object.prototype上也有toString方法,通过改变其this指向可以实现万能数据类型检测的功能
    3. console.log(Object.prototype.toString.call(1)); // [object Number]
    4. console.log(Object.prototype.toString.call(true)); // [object Boolean]
    5. console.log(Object.prototype.toString.call([])); // [object Array]
    6. console.log(Object.prototype.toString.call({})); // [object String]
    7. console.log(Number.prototype.toString.call(123)); // 124
    8. console.log(Boolean.prototype.toString.call(true)) // 'true'

    十一、call和apply的作用

  • 相同点:都是为了改变其this的指向

  • 不同点:call传递参数时是一项一项去传递的;而apply传递参数时是以数组的形式传递
  • 在使用小括号执行符号时,系统会隐式加上call

    1. // call和apply的作用
    2. // 两者都是为了改变其this的指向
    3. // call传递参数时是一项一项的去传递的
    4. // apply传递参数是以数组的形式传递的
    5. function test(){
    6. console.log('a');
    7. }
    8. test(); // 'a'
    9. // 在使用执行符号时,系统会隐式加上call
    10. test.call(); // 'a'

    ```javascript

    1. // call改变其this指向: 在传递参数时是一项一项去传递的
    2. function Car(brand, color){
    3. this.brand = brand;
    4. this.color = color;
    5. }
    6. var newCar = {}
    7. Car.call(newCar, 'Benz', '黑色');
    8. console.log(newCar); // {brand: 'Benz', color: '黑色'}
  1. // apply改变其this指向: 在传递参数时是以数组形式传递的
  2. function Car(brand, color){
  3. this.brand = brand;
  4. this.color = color;
  5. }
  6. var newCar = {
  7. displacement: '3.0'
  8. }
  9. Car.apply(newCar, ['Mazda', '红色'])
  10. console.log(newCar); // {displacement: '3.0', brand: 'Mazda', color: '红色'}
  11. var car = new Car('比亚迪', '白色');
  12. console.log(car); // Car {brand: '比亚迪', color: '白色'}
  1. 实例
  2. ```javascript
  3. function Compute(){
  4. this.add = function(a, b){
  5. console.log(a + b);
  6. },
  7. this.minus = function(a, b){
  8. console.log(a - b);
  9. }
  10. }
  11. function FullCompute(){
  12. // console.log(this);
  13. Compute.call(this);
  14. this.mul = function(a, b){
  15. console.log(a * b);
  16. },
  17. this.div = function(a, b){
  18. console.log(a / b);
  19. }
  20. }
  21. var compute = new FullCompute();
  22. compute.add(2, 4);
  23. compute.minus(2, 4);
  24. compute.mul(2, 4);
  25. compute.div(2, 4);

十二、作业

年龄为多少岁,姓名为xxx买了一辆排量为多少的什么颜色什么牌子的车「利用call或者apply改变this指向」

  1. // 年龄为多少岁,姓名为xxx买了一辆排量为多少的什么颜色什么牌子的车「利用call或者apply改变this指向」
  2. function Car(opt){
  3. this.brand = opt.brand;
  4. this.color = opt.color;
  5. this.displacement = opt.displacement;
  6. }
  7. function Person(opt){
  8. this.name = opt.name;
  9. this.age = opt.age;
  10. this.selectCar = function(){
  11. Car.apply(this, [{
  12. brand: 'Benz', color: '黑色', displacement:'2.0'
  13. }]);
  14. console.log('年龄为:' + this.age + '姓名为:' + this.name + '买了一辆排量为:' + this.displacement + '的' + this.color + this.brand);
  15. }
  16. }
  17. var person = new Person({
  18. name: '小明',
  19. age: 18
  20. });
  21. person.selectCar();
  1. // 11课时作业
  2. function Car(brand, color, displacement){
  3. this.brand = brand;
  4. this.color = color;
  5. this.displacement = displacement;
  6. this.info = function(){
  7. return '排量为' + this.displacement + '的' + this.color + this.brand;
  8. }
  9. }
  10. function Person(opt){
  11. Car.apply(this, [opt.brand, opt.color, opt.displacement]);
  12. this.name = opt.name;
  13. this.age = opt.age;
  14. this.say = function(){
  15. console.log('年龄为' + this.age + '岁姓名为' + this.name + '买了一辆' + this.info());
  16. }
  17. }
  18. var person = new Person({
  19. brand: '奔驰',
  20. color: '红色',
  21. displacement: '3.0',
  22. name: '约翰',
  23. age: 28
  24. });
  25. person.say();