js进阶第一天

成长必经之路

目录

  1. 构造函数: 原型对象及原型链
  2. 构造函数: 继承

构造函数-原型对象

构造函数的实例成员

目标: 实例成员的内存问题

  • 通过构造函数封装后,创建多个对象,对象调用同一个方法,在内存中执行的并不是同一个方法
  1. //1.构造函数设置形参
  2. function Person(name, age) {
  3. this.name = name;
  4. this.age = age;
  5. this.sing = function () {
  6. console.log("我会唱歌");
  7. }
  8. }
  9. //创建:创建是实例化对象!
  10. let p1 = new Person("zs", 18); let p2 = new Person("ls", 28);
  11. //问题
  12. //1.实例化对象功能方法:打印结果一摸一样
  13. console.log(p1.sing); //调用,我会唱歌
  14. console.log(p2.sing); //我会唱歌
  15. // 2.回顾,函数是什么数据类型?复杂
  16. console.log(p1.sing == p2.sing); //false
  17. //3.结论:两个方法是功能完全一样,来自各功能的实例化对象;但是内存是不同的地址;形成内存浪费!
  • 如上问题会导致内存浪费, 传统方式也是这样的问题

原型对象prototype

目标: 知道什么是原型对象prototype及作用

  • prototype: 叫原型对象,是每一个构造函数身上的一个属性,该属性是以对象的形式存在的(原型对象)
    ```javascript //导入: //构造函数:学习其他的知识是可以解决内存浪费的问题 //字面量的方式:到此为止;不能解决内存浪费的问题!

//知识: //prototype是属性名,构造函数上属性:prototype //值:对象;如果给自定义构造函数上添加方法,添加到prototype属性值对象上! //解决内存的问题! function Person(name, age) {this.name = name;this.age = age } //语法 Person.prototype.sing = function () { console.log(“我会唱歌”); } Person.prototype.eat = function () { console.log(“我会跳舞”); } //调用:实例化对象 let p1 = new Person(“zs”, 15) let p2 = new Person(“ww”, 19) //测试1:是否有sing方法 p1.sing(); p2.sing() //我会唱歌 //测试2:看两个方法是否功能是一样? console.log(p1.sing); //ƒ () {console.log(“我会唱歌”);} console.log(p2.sing); //ƒ () {console.log(“我会唱歌”);} console.log(p1.eat); //ƒ () {console.log(“我会跳舞”);} console.log(p2.eat); //ƒ () {console.log(“我会跳舞”);} //测试3:测试两个方法是否来自同一个内存空间 ?(内存地址) console.log(p1.sing == p2.sing); //true // 结论:学习prototype,给未来的实例化对象上设置一些方法,直接设置给 构造函数.prototype上, // Fn/Person: 构造函数 // let p1 = new Person(); 实例化对象; // prototype 原型对象;

  1. - 作用: 通过原型对象设置构造函数中功能的方法, 设置公共方法, 解决内存浪费问题!
  2. - 语法: `构造函数.prototype.方法名 = function(){ };`
  3. <a name="50aff5c1"></a>
  4. ### 对象原型`__proto__`
  5. > 目标: 能够了解**proto**的作用
  6. 1. 导入: 解释数组的实例化, 为什么都可以使用push方法? 而且是同一个功能, 同一个地址!
  7. 2. 为什么实例化 会有构造函数原型 prototype上的方法
  8. 1. 实例化对象上有一个属性名 **proto** 属性值是一个对象; **proto** 上保存有对象的地址, 地址指向哪里? 指向构造函数的`prototype`
  9. 1. `ldh.__proto__==Star.prototype` 返回true 保存了一个地址, 是同一个地址;
  10. ```javascript
  11. function Person(name, age) {this.name = name;this.age = age;};
  12. // 语法:
  13. Person.prototype.sing = function () {console.log("我会唱歌");};
  14. // 实例化
  15. let p1 = new Person("zs", 15);
  16. let p2 = new Person("ls", 26);
  17. // ***问题1:p1 p2 为什么上有sing这个方法?
  18. // 知识:
  19. //实例化对象:对象就会有 __proto__:上面有sing方法 有属性:constructor
  20. //注意:__前后都是两个下划线
  21. // true:p1.__proto__ 和 原型对象 其实是一个东西;
  22. //原型对象上有sing p1.__proto__上也有同样这个sing方法
  23. console.log(p1.__proto__ == Person.prototype); //true
  24. console.log(p2.__proto__ == Person.prototype); //true
  25. // ***问题2:为什么这样调用呢 p1.sing(); 合理:p1.__proto__.sing();
  26. // __proto__:原型链:
  27. // 记住规则:如果 实例化对象.xxx();
  28. // 1.先在自己实例化对象上先找下是否有xxx方法,如果没有;
  29. // 2.会去实例化.__proto__去找;
  30. // __proto__:非标准属性,开发过程中不出现代码;
  31. // 为了给方法寻找提供链的方向;
  32. console.log(p1); //Person {name: "zs", age: 15}

constructor构造函数

目标: 掌握constructor及作用

  • 概念: constructor构造函数,每一个原型对象prototype和对象原型身上都有属性
  • 作用: 通过constructor构造函数记录当前对象属于哪个构造函数的
  1. //构造函数:创建对象,实例化对象
  2. // 封装插件
  3. // 创建对象比直接使用字面量形式创建对象更加性能好
  4. function Person() {}
  5. // 原型对象:添加占内存一个空间方法;
  6. // 默认自带属性:constructor:构造函数;
  7. // console.log(Person.prototype.constructor);
  8. // 意义:通过原型对象, 知道隶属于哪个构造函数而已!
  9. // 实例化.__proto__ 知道实例化对象由谁构造出来!
  10. let p1 = new Person();
  11. console.log(p1); //Person {}
  • 构造函数,原型对象,对象原型之间的关系

原型链

目标: 原型链及作用

概念: 每一个对象都会proto,指向是哪里的地址呢?

  • 原型链的形成:
    • 每一个对象有一个proto属性,地址指向为其构造函数的原型对象prototype,及为同一个对象;
      构造函数的原型对象prototype也是一个对象,也有proto属性,这样一层一层往上找就形成了原型链;
  • __proto__的意义:就在于为对象的查找机制提供一个方向,或者说一条路线,但是它是一个非标准属性,因此
    实际开发中,不可以使用这个属性,我们只需要知道它的指向和构造函数的prototype指向同一个地址;
  • 为什么要设置原型链?给对象上的方法设置查找机制:
    • 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性或方法。
    • 如果没有就查找它的原型对象(也就是 proto指向的 prototype 原型对象)。
    • 如果还没有就查找原型对象的原型(Object的原型对象)。
      • 依此类推一直找到 Object 为止(null)。
      • 如果还没有,就报错这个对象没有这个方法; Uncaught Typ
  1. // 原型链
  2. function Person(name, age) {
  3. this.name = name;
  4. this.age = age;
  5. }
  6. Person.prototype.sing = function () {
  7. console.log("我会唱歌");
  8. }
  9. //实例化对象:
  10. let p1 = new Person("zs", 18)
  11. //原型对象:也是对象,发现这个对象上_proto_
  12. // 对象其实也应该是某个构造函数的实例化对象
  13. let obj1 = Person.prototype;
  14. //Object:date内置构造函数
  15. //{}字面量简单方式 object();内置构造函数的方式,创建空{}
  16. // Object.prototype
  17. console.log(Object.prototype == obj1._proto_); //true
  18. //obj1 其实就是objet一个实例化
  19. // 原型链,不是逻辑, 是客观存在; 是什么 ? 一个对象,有_proto_ 指向其原型对象: 原型对象其实也是个普通对象,
  20. // 也有proto_属性,指向其原型对象Object.prototype;

原型链 -this

  1. // this
  2. let c;
  3. function Person() {
  4. this.name = "zs";
  5. this.age = 18;
  6. }
  7. Person.prototype.sing = function () {
  8. console.log("我会嫦娥"); //我会嫦娥
  9. c = this
  10. }
  11. let a = new Person()
  12. a.sing() //this赋值一份给c
  13. console.log(c == a); //true
  14. //-----------------使用------------------------
  15. //落地:构造函数内部this,型对象上方法内this其实都是未来其次实例化对象!
  16. function Person() {
  17. this.name = "zs"
  18. this.age = "28"
  19. }
  20. Person.prototype.sing = function () {
  21. console.log(this.name + "会唱歌"); //true
  22. }
  • 练习:
    1.查看原型对象上方法内部this的指向

2.给数组Array的原型对象上拓展求和的方法;

  1. // 需求:让 你在 数组原型对象 上 添加一个求和的方法;getSum
  2. // 1. 找到 数组 原型对象 Array.prototype
  3. // console.log(arr.__proto__); // 非标准属性,开发不用!
  4. // console.log(Array.prototype); // Array Object 内置构造函数名!
  5. // 2. 往对象上填加新的方法
  6. Array.prototype.getSum = function() {
  7. // 3. 求和:数组求和封装为一个方法
  8. // 封装步骤:
  9. // 1.实际过程;
  10. // 2.套个函数壳子;
  11. // 3.设置形参(调用的时候是否需要传入实参)?返回值(调用的时候,看是否要得到函数执行结果)?
  12. // this考虑:实例化对象;
  13. var sum = 0;
  14. for (var i = 0; i < this.length; i++) {
  15. sum += this[i];
  16. }
  17. return sum;
  18. };
  19. // 遇到报错:
  20. // 1.解读英文 "xxx" of null / undefined
  21. // 2.告诉报错位置;
  22. var arr = [10, 20, 30];
  23. var res = arr.getSum();
  24. console.log(res);

构造函数-继承

继承

目标: 掌握属性和方法继承

实例属性的继承: 直接复制?

  1. //1------------------手动复制方式:维护不方便!
  2. // other内部属性名发生改变,My内部不会自动改变!
  3. function Other(house, money,car){
  4. this.house = house;
  5. this.money = money;
  6. this.car = car;}
  7. function My(housemoney,car) {
  8. this.house = house;
  9. this.money = money;
  10. this.car = car;}

实例属性的继承: call语法

  1. // 2---------------------新的语法:一次性把对方身上所有属性全部拿过!
  2. // call:打电话呼叫;基础语法
  3. //
  4. function other(housemoney,car){
  5. this.house = house;
  6. this.money = money;
  7. this.car = car;}
  8. var obj = {
  9. info:"我就是个单独对象"
  10. };
  11. other.call(obj102010);
  12. // call语法规则:【需要重点记忆】
  13. // 1.Other丞数肯定是要执行!
  14. // 2.参数:第一个参数other执行,other内部this上的所有的属性名即将要去到这个参数上;
  15. // 3.剩余参数,必须逗号分隔;就是oTher执行时,需要传入的实参;
  16. console.log(obj);

实例属性的继承: call方式实现

  1. //构造函数的内部数据的继承:
  2. function other(house money,car) {
  3. this.house = house;
  4. this.money = money;
  5. this.car = car;}
  6. function My2(a, b,){
  7. //这个My2构造函数内部的实例化对象
  8. other.call(thisa b,c);
  9. //相当于:
  10. // this.house = a;
  11. //this.money = b;
  12. // this.car - c;}

继承

原型对象上的方法继承

  1. //别人写好杓造函数+原型对象
  2. function other(house){
  3. this.house = house;
  4. }
  5. other.prototype.show = function() {
  6. console.log("你看老子多有钱,我有”+this.house +“套房子");
  7. };
  8. Other.prototype.sing - function() {
  9. console.log(“你看老子多有钱,我会唱歌“);
  10. };
  11. //自己:
  12. function My (house){
  13. other.call(thishouse);
  14. }

方式1: 把其他构造函数的原型对象直接使用( 不可取)

  1. My.prototype = other.prototype;
  2. My.prototype.dance = function() {
  3. console.log("我会跳舞");
  4. }
  5. // m实例化:上面有show sing dance这些方法没有问题;
  6. // var m = new My(10);
  7. // m.show();
  8. // m.sing();
  9. //ll m.dance();
  10. //执行结果:o这个实例化也有dance这个方法,other.prototype被修改了!
  11. var o = new other(2e);
  12. console.log(o);

方式2: 直接修改原型对象的proto属性值为 对方的原型对象( 逻辑正确, 代码不合适)

  1. //默认情况:My.prototype._proto_ -= Object.prototype
  2. //现在修改:相当于做了手术,做了修改,把other原型对象上方法继承过来;
  3. My.prototype._proto_ = other.prototype;
  4. // 1.测试My
  5. My.prototype.dance = function() {
  6. console.log("我是y原型对象上方法dance");
  7. };

方式3: 最终方案

  1. My.prototype = new other();
  2. // My.prototype 本质是_proto__ : Object的原型对象﹔自带了constructor:My;1/
  3. // 想被替换为_proto__本来就是指向other原型对象;
  4. // let o = new other(); o._proto__本来就是指向other原型对象﹔
  5. My.prototype.constructor = My;
  6. //对上面的分解
  7. //var o = new other(50); // o{house:50 ,__proto__:other.prototype}
  8. // My.prototype = o;
  9. My .prototype.dance = function() {
  10. console.log("我是ly原型对象上一个方法");
  11. }

继承-示意图