初识JavaScript (二十)

1 构造函数的执行过程

构造函数的执行过程:
通过代码调试:debug过程。
断点调试:breakpoint
bug:代表了程序中的错误。原意是臭虫的意思。

调试按钮:
step:逐步执行代码,执行下一行代码。遇到方法调用,会进入方法内执行。
step out:跳出当前方法,执行方法后的代码。
step into:进入下一个方法执行。
step over:逐步执行代码,执行下一行代码。遇到方法调用,不会进入执行。

构造方法的执行过程:
1:实参给形参赋值,js底层创建好空对象,并赋值给this。
2:通过this给对象绑定属性。绑定方法。
3:绑定属性结束之后,对象初始化完毕。
4:返回this 赋值给变量。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script>
    function Student(name,age,score) {
        //给this绑定属性的过程。
        this.name = name;
        this.age = age;
        this.score = score;
        this.study = function () {
            console.log (this.name + "热爱学习");
        };
        this.eat = function () {
            console.log (this.name + "是个吃货");
        }
    }
    console.log ("hello");
    console.log ("hello");
    console.log ("hello");
    var student = new Student("小白",10,100);
    student.study();
</script>
</body>
</html>

2 构造函数的优缺点

构造函数创建对象的优缺点:
优点:
1:构造函数提供了一个模板,通过该模板,可以快速方便的创建对象。
2:可以更加明确创建对象的类型。

缺点:
不同的对象之间的属性和方法都是不共享的,当时通过同一个构造方法创建的对象的功能是完全一样的。不同对象可以共享同一个方法。这样更加节约内存。
缺点:不同对象相同的方法不能共享使用。
如何让同类型的不同的对象共享方法?

解决该问题的方法:
将需要共享的方法放到一个所有的对象共享的一个内存空间中保存。
如果对象需要使用方法的时候,去共享空间中去查找使用即可。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script>
    function Student(name,age,score) {
        //给this绑定属性的过程。
        this.name = name;
        this.age = age;
        this.score = score;
        this.study = function () {
            console.log (this.name + "热爱学习");
        };
        this.eat = function () {
            console.log (this.name + "是个吃货");
        }
    }

    var bai = new Student("小白",18,100);
    var qing = new Student("小青",20,100);

</script>
</body>
</html>

3 函数的原型对象

函数的原型对象:
函数的定义是具有唯一性的,任何一个函数所占用的内存空间是唯一的。
任何的函数都有一个属性 prototye 。该属性 prototype 指向了一个对象。该对象就称为[函数的原型对象],简称 [函数原型]. 任何的函数的原型 都是具有唯一性的,有自己唯一的内存空间。
函数原型中有一个属性:constructor。指向了该原型对象的构造函数。

将对象需要共享的功能添加到构造函数的原型中。实现多个对象共享相同的方法。
使用原型中定义的功能:直接使用对象调用方法即可。
通过构造函数创建的任何对象对象中都有一个属性:[prototype] 该属性在规范中并没有规定必须提供访问它的方式。该属性指向了对象的构造函数的原型对象。对象可以通过该属性去访问原型中的内容。
将需要共享的函数添加到构造函数的原型中。通过构造函数创建的对象通过对象内的[prototype]就可以访问原型中添加的方法了。
在谷歌和火狐的浏览器中,对象提供了 proto 属性来访问对象的构造函数的原型对象。

总结:
希望每个对象都有自己独立的内存的属性需要在构造函数内定义。
希望所有对象共享的部分添加在构造函数的原型中去。
通常情况下,属性都是定义在构造函数内的。称为对象的私有属性。如果是对象共有的属性,那么也可以定义在原型中。
方法需要定义在原型中。
原型对象中添加的属性和方法直接使用 对象 访问即可。【不需要添加 propt

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script>
    function Student(name,age,score) {
        //给this绑定属性的过程。
        this.name = name;
        this.age = age;
        this.score = score;
        // this.study = function () {
        //     console.log (this.name + "热爱学习");
        // };
        // this.eat = function () {
        //     console.log (this.name + "是个吃货");
        // }
    }

    //Student函数原型
    console.log (Student.prototype);
    //函数的原型中的constructor 和 构造函数比较
    console.log (Student === Student.prototype.constructor);//true

    //将对象需要共享的功能添加到构造函数的原型中。
    Student.prototype.study = function () {
        console.log (this.name + "热爱学习");
    };
    Student.prototype["eat"] = function () {
        console.log (this.name + "是个吃货");
    }
    //在原型对象中添加共有的属性。
    Student.prototype.country = "中国";
    //使用对象调用原型中定义的方法。
    var bai = new Student("小白",18,100);
    var qing = new Student("小青",17,100);
    bai.study();
    bai.eat();
    qing.study();
    qing.eat();
    // 两个对象共享同一个方法
    console.log (bai.study === qing.study);//true

    console.log (bai.__proto__ === Student.prototype);//true
    console.log (qing.__proto__ === Student.prototype);//true

</script>
</body>
</html>

4 为何对象可以访问原型中的内容?

构造函数创建的对象为何可以直接访问构造函数的原型中的内容?
1:当通过对象去访问一个方法或者是属性的时候,首先去自己的私有属性中去查找,如果私有属性中包含了被访问的内容,就直接访问。
2:如果私有属性中没有要访问的内容。那么就顺着对象的 [prototype](谷歌火狐中的__proto__)指向的原型对象中去查找。如果有,就直接访问。
3:如果原型对象中也没有要访问的内容,那么就去原型中的原型对象中去查找,重复上述的过程,直到找到为止,如果找到Object 根对象的原型中都没有找到,那么就找不到该内容了,报错了。
4: 那么整个查找的过程,就是顺着[prototype][proto]链条向根查找。 【原型链】。

注意: Object 的原型对象的原型为 null。
Object 构造函数的原型中定义了所有的对象的一些通用的方法。底层就在创建对象的时候,将Object的原型对象添加到了创建对象的原型链的尾部。

原型中定义的方法都是实例方法,都是通过对象实例来访问的。

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>Title</title>
</head>
<body>
<script>
   function Student(name,age,score) {
       //给this绑定属性的过程。
       this.name = name;
       this.age = age;
       this.score = score;
       // this.study = function () {
       //     console.log (this.name + "热爱学习");
       // };
       // this.eat = function () {
       //     console.log (this.name + "是个吃货");
       // }
   }

   //Student函数原型
   console.log (Student.prototype);
   //函数的原型中的constructor 和 构造函数比较
   console.log (Student === Student.prototype.constructor);//true

   //将对象需要共享的功能添加到构造函数的原型中。
   Student.prototype.study = function () {
       console.log (this.name + "热爱学习");
   };
   Student.prototype["eat"] = function () {
       console.log (this.name + "是个吃货");
   }
   //在原型对象中添加共有的属性。
   Student.prototype.country = "中国";
   //使用对象调用原型中定义的方法。
   var bai = new Student("小白",18,100);
   bai.study();
   bai.eat();
   // bai.country;
   //Object 构造函数的原型中定义的方法,可以被直接使用。
   console.log (bai.toString());//[object Object]

   //Object 的原型对象的原型为null。
   console.log (Object.prototype.__proto__);

</script>
</body>
</html>

5 this的阶段总结

this的阶段总结:
1:在全局作用域下的this,代表window对象。
2:在构造函数创建对象的时候,里面的this代表刚刚创建好的对象。
3:原型对象中添加的方法中的this。调用该方法的实例对象。
4:构造函数中定义的方法中的this。调用该方法的实例对象。
this总结:this代表当前环境下的对象。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script>
    function Student(name,age,score) {
        this.name = name;
        this.age = age;
        this.score = score;
        this.eat = function () {
            console.log (this);
        }
    }
    Student.prototype.study = function () {
        console.log (this.name + "热爱学习");
    };
    var bai = new Student("小白",18,100);
    bai.study();
    bai.eat();
    var qing = new Student("小青",18,100);
    qing.study();
    qing.eat();
</script>
</body>
</html>

6 原型练习

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--
    1: 定义等边三角形的构造函数。属性:一个边长。方法:周长+面积。
        功能都添加到原型中去。
        创建实例,测试功能。

    2:给String添加实例方法,reverse(). 翻转当前对象。

    3:给String添加实例方法。sort().对当前字符创对象排序。
-->
<script>
    function Triangle(length) {
        this.length = length;
    }
    Triangle.prototype.perimeter = function () {
        return this.length * 3;
    }
    Triangle.prototype.area = function () {
        var height = this.length * Math.sqrt (3) / 2;
        return this.length * height / 2;
    }

    String.prototype.reverse = function () {
        return this.split("").reverse().join("");
    }

    console.log ("abcd".reverse());

    String.prototype.sort = function () {
        return this.split("").sort().join("");
    }
    console.log ("bd1a5c6ef".sort());
</script>
</body>
</html>

7 js中的继承

js中的继承是基于原型的继承。
js中的继承的方式:3种。
a:借用构造函数继承
b:原型链继承
***c:组合继承
继承的最直接的好处:代码的复用。

继承:inheritance。
概念:
父对象:被继承者。
子对象:继承者。
继承的概念:
子对象可以直接使用父对象中的内容的过程。

父类型:描述的对象的范围更广。
子类型:描述的对象的范围小。但是功能和属性更多。

instancof:用来判断一个对象是否是某种类型的实例对象的。

例子:在前面的练习中,学生对象可以直接使用Object原型中的功能。学生对象隐式的继承了Object原型中的内容。

结论:js中的所有的对象,都直接的或者间接的继承了Object的原型中的内容。除了Object的原型对象本身。这样保证了所有的对象都可以访问Object原型中定义的方法。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script>
    function Person(name,age,gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }
    Person.prototype.eat = function () {
        console.log (this.name + "\t是一个吃货!");
    }

    function Student(name,age,gender,classId,score) {
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.classId = classId;
        this.score = score;
    }
    Student.prototype.eat = function () {
        console.log (this.name + "\t是一个吃货!");
    }
    Student.prototype.study = function () {
        console.log (this.name + "\t是一个学霸!");
    }

    console.log (new Person() instanceof Object);//true
    console.log (new Student() instanceof Person);//false
</script>
</body>
</html>

8 借用构造函数继承

借用构造函数继承:
在子类型的构造函数内借用父类型的构造函数来使用。

通过测试结论:
1:子类型可以继承父类型构造函数中定义的所有的私有成员(属性+功能)。
2:子类型不能继承父类型构造函数原型中的内容。

借用构造函数继承的方式存在一定的缺陷。
借用构造函数继承,使用instanceof判断。子类对象不是所谓的父类类型的实例。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script>

    function Person(name, age, gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.sleep = function () {
            console.log (this.name + "\t爱睡大觉!");
        }
    }

    Person.prototype.eat = function () {
        console.log (this.name + "\t是一个吃货!");
    }

    function Student(name, age, gender, classId, score) {
        //借用构造函数继承。
        Person.call(this,name,age,gender);
        this.classId = classId;
        this.score = score;
    }
    // Student.prototype.eat = function () {
    //     console.log (this.name + "\t是一个吃货!");
    // }
    Student.prototype.study = function () {
        console.log (this.name + "\t是一个学霸!");
    }

    //创建子类对象:
    var stu = new Student("小白",19,"男","0001",100);
    console.log (stu);

    //**************
    console.log (stu instanceof Person);//false.

</script>
</body>
</html>

9 原型链继承

js的第二种继承方式:原型链继承。
原型链:任何函数的原型对象中也有一个 [prototype](谷歌火狐中叫__proto__)对象。指向了原型的原型对象。原型的原型对象中还有原型对象。到最顶端的Object对象的原型是没有原型对象的。组成了一个由
[prototype](谷歌火狐中叫__proto__)属性链接起来的【原型对象的链条—原型链】。

原型链继承的关键步骤:让子类型的构造函数的原型指向父类型的一个实例。

原型链继承的问题:
1:不能继承父类型的构造函数中的私有成员。
2:子类型的构造函数的原型对象的constructor属性指向了父类型构造函数。必须修正。
补充:instanceof 的作用:右边的类型的原型对象是否在左边的对象的原型链上,在返回true,否则false。

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>Title</title>
</head>
<body>
<script>
   function Person(name, age, gender) {
       this.name = name;
       this.age = age;
       this.gender = gender;
   }
   Person.prototype.eat = function () {
       console.log (this.name + "\t是一个吃货!");
   }

   function Student(name, age, gender, classId, score) {
       this.name = name;
       this.age = age;
       this.gender = gender;
       this.classId = classId;
       this.score = score;
   }
   //原型链继承的实现:Student的原型指向Person的一个实例。
   //不要传参数。
   Student.prototype = new Person(/*"小甜甜",17,"女"*/);
   //做原型的构造函数的修正。
   Student.prototype.constructor = Student;
   //让进行上面最重要的那一步,然后再在子类型的原型中添加必要的功能。
   Student.prototype.study = function () {
       console.log (this.name + "\t是一个学霸!");
   }

   var stu = new Student("牛嫂",37,"女","0002",60);

   console.log (Student.prototype.constructor === Student);

   console.log (stu instanceof Person);//true

</script>
</body>
</html>

10 组合继承

组合继承:将借用构造函数继承和原型链继承组合使用。
借用构造函数继承:不能继承父构造函数原型中的内容。
原型链继承:不能继承父构造函数中的私有成员。

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>Title</title>
</head>
<body>
<script>
   //父类型的构造函数。
   function Person(name, age, gender) {
       this.name = name;
       this.age = age;
       this.gender = gender;
   }

   Person.prototype.eat = function () {
       console.log (this.name + "\t是一个吃货!");
   }

   function Student(name, age, gender, classId, score) {
       //借用构造函数继承。
       Person.call(this,name,age,gender);
       this.classId = classId;
       this.score = score;
   }
   //原型链继承
   //子构造函数的原型指向父类型的一个实例。不需要传参
   Student.prototype = new Person();
   //子构造函数原型的constructor属性的修正。
   Student.prototype.constructor = Student;
   //添加其他的方法。
   Student.prototype.study = function () {
       console.log (this.name + "\t是一个学霸");
   }

   var stu = new Student("紫霞仙子",27,"女","0001",90);

   console.log (Student.prototype.constructor === Student);//true

</script>
</body>
</html>

11 类型的静态方法

函数的分类:
实例函数:对象调用。
静态函数:类型调用。通常是该类型的工具方法。
Array.isArray(arr);
Math.random().
Date.now()
String.fromCharCode()

原型中定义的函数都是实例函数。对象调用。

//如何在自定义的类型中添加静态成员。
语法:
构造函数名.方法名 = function(){}
调用:
构造函数名.方法名(实参);
构造函数的静态属性:是该类型的属性,

结论:类型的静态成员是该类型的方法和属性,不属于类型实例的属性和方法。
静态的成员在内存中只有唯一的一份内存空间。因为不依赖于每一个对象,依赖于构造函数。相当于给构造函数对象添加的属性和方法。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script>

    console.log (Number.MIN_VALUE);//静态属性
    //父类型的构造函数。
    function Person(name, age, gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    Person.prototype.eat = function () {
        console.log (this.name + "\t是一个吃货!");
    }

    function Student(name, age, gender, classId, score) {
        //借用构造函数继承。
        Person.call(this,name,age,gender);
        this.classId = classId;
        this.score = score;
    }
    //原型链继承
    //子构造函数的原型指向父类型的一个实例。不需要传参
    Student.prototype = new Person();
    //子构造函数原型的constructor属性的修正。
    Student.prototype.constructor = Student;
    //添加其他的方法。
    Student.prototype.study = function () {
        console.log (this.name + "\t是一个学霸");
    }

    var stu = new Student("紫霞仙子",27,"女","0001",90);


    //需求:定义静态方法,来判断任意变量是否是Person的实例的。
    Person.isPerson = function (obj) {
        return obj instanceof Person;
    }
    console.log (Person.isPerson(stu))//true
    //类型的属性。
    Person.MAX_AGE = 150;

</script>
</body>
</html>

12 构造函数原型图

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值