2021-02-26构造函数

构造函数

1.什么是构造函数

定义:在js中,使用new关键字来调用的函数,被称为构造函数。

构造函数的作用:创建对象。

2.为什么要使用构造函数

假如需要创建多个类似的对象,我们会书写很多重复的无意义代码。此时我们使用构造函数,方便快捷的创建一个对象。

如何封装一个构造函数

将一个对象的特征作为属性,将它的行为作为方法。

function Dog(name,age,breed,color){
    this.name = name;
    this.age = age;
    this.breed = breed;
    this.color = color;
    this.show = function () {
    	alert("我只是一只小" + this.breed + "啊!")
    }
    this.bark = function () {
    	alert("汪汪汪!")
    }
}

【注意】构造函数的名字首字母大写。

3.构造函数的执行过程

function Animal(color){
	this.color = color;
}

当一个函数创建完成后,我们并不知道它是不是一个构造函数。像上面案例中,即使函数名首字母大写,我们也不确定它是构造函数。只有当一个函数前用new关键字来调用时,我们才能说它是一个构造函数。

var dog = new Animal("black")

构造函数的执行过程有以下4个步骤。

  1. 当用new关键字调用构造函数时,会开辟一个新的内存空间,这个内容空间就是新的对象实例。
  2. 将构造函数内部的this指向当前的内存空间(新的对象)。
  3. 执行函数内的代码。(对象的属性和方法的赋值。)
  4. 将内存空间的地址作为返回值返回。

4.构造函数的返回值

构造函数不需要手动添加返回值,默认返回新对象的内存地址。

(1)手动添加一个基础数据类型的返回值,最终还是返回新对象的内存地址。

function Person(name){
    this.name = name;
    return "蔡徐坤";
}
var p = new Person("范丞丞");
console.log(p.name);//范丞丞

(2)手动添加一个复合数据类型的返回值,返回手动添加的对象的内存地址。

function Person(name){
    this.name = name;
    return {name:"蔡徐坤"};
}
var p = new Person("范丞丞");
console.log(p.name);//蔡徐坤

5.与普通函数的区别

5.1调用方式的不同

普通函数使用函数名调用

构造函数通过new关键字来调用

5.2 返回值不同

普通函数的返回值是函数内return的结果

构造函数的返回值是函数内创建对象的地址。

5.3 作用的不同

构造函数时专门用来创建对象。

普通函数的作用可以自由定义。

原型对象

我们创建的每一个函数都有一个prototype属性,这个属性指向一个对象。而这个对象所有的属性和方法都会被构造函数拥有。

function Dog(name,age){
    this.name = name;
    this.age = age;
    // this.bark = function() {
    //     console.log("汪汪汪!");
    // }//缺点:内容一样但每一次都要调用一次
}
var dog1 = new Dog("来福",3)
var dog2 = new Dog("常威",2)
Dog.prototype.bark = function () {
    alert("汪汪汪!")
}
Dog.prototype.breed = "哈士奇";
alert(dog1.bark===dog2.bark);//true
alert(dog1.breed)//哈士奇
alert(dog2.breed)//哈士奇
function fn() {

        }
        console.log(fn.prototype);

在这里插入图片描述

对象的封装

一般情况下,公共属性定义到构造函数里面,公共的方法定义到原型对象身上。

混合模式(构造函数+原型 模式)

        function Dog(name,age,breed,color){
                this.name = name;
                this.age = age;
                this.breed = breed;
                this.color = color;
        }
        Dog.prototype.show = function () {
            alert("我只是一只小" + this.breed + "啊!")
        }
        Dog.prototype.bark = function () {
            alert("汪汪汪!")
        }

__proto__

每一个对象都有一个属性__proto__,这个属性指向构造函数的prototype,也就是构造函数的原型对象。我们之所以可以在对象中使用原型对象中的方法和属性就是因为对象中有__proto__属性的存在。

原型链

在这里插入图片描述
如果实例对象中调用了该对象没有的方法或属性,去__proto__去找方法或属性,如果还没有则继续往上寻找,直到null。

function Dog(name, age, breed, color) {
            this.name = name;
            this.age = age;
            this.breed = breed;
            this.color = color;
        }
        Dog.prototype.show = function() {
            alert("我只是一只小" + this.breed + "啊!")
        }
        Dog.prototype.bark = function() {
            alert("汪汪汪!")
        }
        var lf = new Dog("来福", 3, "秋田", "明黄色");
        // console.log(lf.toString());
        // console.dir(lf.__proto__ === Dog.prototype); //true
        console.dir(lf);//Dog的实例对象
        console.dir(lf.__proto__);//Dog的原型对象
        console.dir(lf.__proto__.__proto__);//Obiect的原型对象
        console.dir(lf.__proto__.__proto__.__proto__);//null

在这里插入图片描述

继承

面向对象的三大特性:封装(封装构造函数),继承,多态

ES6之前没有给我们提供 extends 继承。我们可以通过构造函数+原型对象的模式去模拟实现继承。这种方法也被称为组合继承。

function Dog(age){
    this.age = age;
}
Dog.prototype.bark = function(){
    alert("汪汪汪!")
}
function Huskie(name,age){
    this.name = name;
    this.age = age;
}
Huskie.prototype.bark = function(){
    alert("汪汪汪!")
}

像上面的案例中,哈士奇是属于狗的一种,如果我们重复定义了一样的属性和方法,写了一些重复的代码也造成了资源的浪费,所以我们让哈士奇继承狗的所有属性和方法。

使用call()方法实现继承

function Dog(age){
	this.age = age;
}
Dog.prototype.swimming = function(){
	alert("会游泳!")
}
function Huskie(name,age){
    Dog.call(this,age);
    this.name = name;
}
var hsq = new Huskie("二哈",2);
hsq.swimming();//报错 

在这里插入图片描述

特点:
  • 该方式是靠调用需要继承的构造函数来实现的,调用过程中使用call方法来改变this的指向。
  • call是不可以继承父对象原型中的属性和方法。
  • call是只能继承构造函数中的属性和方法。

使用prototype实现继承

在原型对象中有一个constructor属性,该属性指向该原型对象的构造函数。

function Dog(age){
            this.age = age;
        }
        Dog.prototype.bark = function(){
            alert("汪汪汪!")
        }
        function Huskie(name,age){
            this.name = name;
        }
        // 这样写相当于让子类与父类指向了同一个原型对象。如果修改了子类的原型对象,则父类的原型对象也会随之修改
        // Huskie.prototype = Dog.prototype;
        Huskie.prototype = new Dog(3);
        Huskie.prototype.constructor = Huskie;
        // var h = new Huskie("二哈", 4);
        // console.log(h.age);//4
        // h.bark(); //汪汪汪!
        // var dog = new Dog();
        // console.dir(dog)
        // console.dir(h.constructor === h.__proto__.constructor); //true

优点:继承了父级原型上的属性和方法

缺点:实现化多个子类时,必须使用共同的属性值。

组合式继承

function Dog(age){
	this.age = age;
}
Dog.prototype.bark = function(){
	alert("汪汪汪!")
}
function Huskie(name,age){
    Dog.call(this,age);
    this.name = name;
}
//此时不需要给父类添加参数
Huskie.prototype = new Dog();
Huskie.prototype.constructor = Huskie;
var h = new Huskie("二哈",5);
console.log(h.age);//5
h.bark();//汪汪汪!

在这里插入图片描述

多态

继承:从父类那里继承父类的属性和方法。

多态:子类自己重新定义从父类继承过来的方法。或者新增一个父类没有的方法。

Huskie.prototype.bark = function(){
	alert("嗷呜!")
}
Huskie.prototype.skill = function(){
	alert("你家没了")
}
var h = new Huskie("二哈",5);
h.bark(); //嗷呜!
h.skill(); //你家没了
var dog = new Dog(3);
dog.bark();//汪汪汪
dog.skill();//报错

ES6面向对象

class 类

// function Dog(breed,color,age){
//     this.breed = breed;
//     this.color = color;
//     this.age = age;
// }
// Dog.prototype.bark = function(){
//     alert("汪汪汪")
// }
// var dog = new Dog("泰迪","棕色",5);
// ------------- ES6写法  -------------------------
class Dog {
    constructor(breed, color, age) {
        this.breed = breed;
        this.color = color;
        this.age = age;
    }
    bark(){
    	alert("汪汪汪")
    }
}
var dog = new Dog("泰迪","棕色",5);
console.log(typeof Dog);//function
console.log(Dog === Dog.prototype.constructor);//false  

通过以上的代码,我们发现,class的本质是函数,类本身指向的就是构造函数。

ES6的class可以看作是构造函数的语法糖。它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型写法更加清晰,更加符合面向对象编程的语法而已。

var arr = new Array();
var arr2 = [];//就是上面new Array的语法糖。

ES6继承

class Dog {
    constructor(breed, color, age) {
        this.breed = breed;
        this.color = color;
        this.age = age;
    }
    bark(){
    	alert("汪汪汪")
    }
}
class Haskie extends Dog{
    constructor(breed, color, age, name){
        //super在这里就相当于调用了父类的构造函数。
        //super不可以省略
        super(breed, color, age);
        this.name = name;
    }
}
const h2 = new Haskie("哈士奇","黑白",5,"二哈");
console.log(h2.name, h2.breed, h2.color, h2.age); //二哈 哈士奇 黑白 5
h2.bark(); //汪汪汪

super

super关键字,既可以当函数来使用,也可以当对象来使用。

第一种情况:super作为函数调用时,表示父类的构造函数。

作为函数时,super()只能用在子类的构造函数中

class A{}
class B extends A{
    constructor(){}
    fn(){
    	super();//报错
    }
}

第二种情况

super作为对象时,在普通方法中,指向父类的原型对象。

class A{
    p(){
    	return 1;
    }
}
class B extends A{
    constructor(){
        super();
        console.log(super.p());//1
    }
}
let b = new B();
console.log(b.p());//1

封装拖拽

拖拽1.0

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        #box {
            width: 150px;
            height: 150px;
            background-color: brown;
            position: absolute;
        }
        
        * {
            margin: 0;
            padding: 0;
        }
    </style>
</head>

<body>
    <div id="box"></div>
    <!-- 
    鼠标按下 小盒子可以移动
    鼠标移动 小盒子移动
    鼠标抬起 小盒子停止移动
    -->
    <script>
        var box = document.getElementById("box");
        box.onmousedown = function(ev) {
            var e = ev || window.event;
            var offsetX = e.offsetX;
            var offsetY = e.offsetY;
            document.onmousemove = function(ev) {
                var e = ev || window.event;
                box.style.left = e.pageX - offsetX + "px";
                box.style.top = e.pageY - offsetY + "px";
            }
            box.onmouseup = function() {
                document.onmousemove = null;
            }
        }
    </script>
</body>

</html>

拖拽2.0

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        #box {
            width: 150px;
            height: 150px;
            background-color: brown;
            position: absolute;
        }
        
        * {
            margin: 0;
            padding: 0;
        }
    </style>
</head>
<!-- 
    鼠标按下 小盒子可以移动
    鼠标移动 小盒子移动
    鼠标抬起 小盒子停止移动


    改造:
        抽离出所有的全局属性
        抽离出所有的全局函数    
 -->

<body>
    <div id="box"></div>
    <script>
        let box = document.getElementById("box");
        let offsetX = 0;
        let offsetY = 0;
        document.onmousedown = boxDown;
        box.onmouseup = boxUp;

        function boxDown(ev) {
            let e = ev || ev.window.event;
            offsetX = e.offsetX;
            offsetY = e.offsetY;
            document.onmousemove = boxMove;
        }

        function boxMove(ev) {
            var e = ev || window.event;
            box.style.left = e.pageX - offsetX + "px";
            box.style.top = e.pageY - offsetY + "px";
        }

        function boxUp() {
            document.onmousemove = null;
        }
    </script>
</body>

</html>

拖拽3.0

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        #box {
            width: 150px;
            height: 150px;
            background-color: brown;
            position: absolute;
        }
        
        * {
            margin: 0;
            padding: 0;
        }
    </style>
</head>
<!-- 
    改造:
        所有的相关代码 => 构造函数中
        全局变量 =>构造函数的属性
        全局函数 => 构造函数的方法

        问题:在事件绑定函数中,this指向的是事件源。
        解决方法:改变this指向。
        call() apply() bind() 
-->

<body>
    <div id="box"></div>
    <script>
        function Drag() {
            this.box = document.getElementById("box");
            this.box.onmousedown = this.boxDown.bind(this);
            this.box.onmouseUp = this.boxUp;
        }
        Drag.prototype.boxDown = function(ev) {
            let e = ev || window.event;
            offsetX = e.offsetX;
            offsetY = e.offsetY;
            document.onmousemove = this.boxMove.bind(this);
        }
        Drag.prototype.boxMove = function(ev) {
            var e = ev || window.event;
            this.box.style.left = e.pageX - offsetX + "px";
            this.box.style.top = e.pageY - offsetY + "px";
        }
        Drag.prototype.boxUp = function() {
            document.onmousemove = null;
        }

        window.onload = function() {
            new Drag("box");
        }
    </script>
</body>

</html>

封装拖拽

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="js/common.js"></script>
    <script>
        window.onload = function() {
            new Drag("box")
            new Drag("box2")
        }
    </script>
    <style>
        #box {
            width: 150px;
            height: 150px;
            background-color: brown;
            position: absolute;
        }
        
        #box2 {
            width: 80px;
            height: 80px;
            background-color: cadetblue;
            position: absolute;
            left: 300px;
        }
        
        * {
            margin: 0;
            padding: 0;
        }
    </style>
</head>

<body>
    <div id="box">

    </div>
    <div id="box2">

    </div>
</body>

</html>
function Drag(id){
    this.box = document.getElementById(id);
    this.box.onmousedown = this.boxDown.bind(this);
    this.box.onmouseup = this.boxUp;
}
Drag.prototype.boxDown = function(ev){
    //处理下兼容
    let e = ev || window.event;
    //记录鼠标按下时相对于盒子的位置。
    offsetX = e.offsetX;
    offsetY = e.offsetY;
    document.onmousemove = this.boxMove.bind(this);
}
Drag.prototype.boxMove = function(ev){
    var e = ev || window.event;
    this.box.style.left = e.pageX - offsetX + "px";
    this.box.style.top = e.pageY - offsetY + "px"
}
Drag.prototype.boxUp = function(){
    document.onmousemove = null;
}

this指向

<body>
    <button id="btn">测试</button>
    <script>
        function Fn() {
            this.a = 10;
            var btn = document.getElementById("btn");
            btn.onclick = this.show;
        }
        Fn.prototype.show = function() {
            console.log(this);
            alert(this.a);
        }
        let fn = new Fn();
        fn.show();
        //未点击按钮:Fn {a: 10}    10
        //点击按钮后:<button id="btn">测试</button>   undefined
    </script>
</body>

面向对象选项卡

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        #box {
            margin: 100px auto 0;
            width: 600px;
        }
        
        #box .btnGrop {
            display: flex;
            height: 30px;
            justify-content: space-between;
            /* background-color: #27ae60; */
        }
        
        #box2 {
            margin: 100px auto 0;
            width: 600px;
        }
        
        #box2 .btnGrop {
            display: flex;
            height: 30px;
            justify-content: space-between;
            /* background-color: #27ae60; */
        }
        
        button {
            width: 100px;
            height: 30px;
            background-color: #1abc9c;
            color: #fff;
            font-size: 16px;
        }
        
        #box div {
            width: 600px;
            height: 600px;
            background-color: #e74c3c;
            border: 1px solid;
            display: none;
        }
        
        #box2 div {
            width: 600px;
            height: 600px;
            background-color: #e74c3c;
            border: 1px solid;
            display: none;
        }
        
        .btnGrop>.active {
            background-color: #f39c12;
            color: #2c3e50;
        }
        
        #box>.active {
            display: block;
        }
        
        #box2>.active {
            display: block;
        }
    </style>
    <!-- 
        需求分析:
            1.给每一个按钮添加点击事件
            2.  (1)先让其他的按钮中的active类名都移除
                (2)给当前按钮添加一个active类名
                (3)对应的div显示出来
                    先让所有的div中的active类名都移除
                    给对应的div添加一个actvie类名。

            
            
        第一次 for循环  i=0
        第二次 for循环  i=1
        第三次 for循环  i=2
        第四次 for循环(没有执行其中的代码)  i=3
            
     -->
    <script>
        window.onload = function() {
            new TabSwitch("box");
            new TabSwitch("box2");
        }
        class TabSwitch {
            constructor(id) {
                var node = document.getElementById(id);
                this.btns = node.getElementsByTagName("button");
                this.contentBoxs = node.querySelectorAll(".contentBox");
                for (let i = 0; i < this.btns.length; i++) {
                    this.btns[i].index = i;
                    this.btns[i].onclick = this.tab.bind(this, this.btns[i]);
                }
            }
            tab(oBtn) {
                for (var i = 0; i < this.btns.length; i++) {
                    this.btns[i].className = "";
                    this.contentBoxs[i].classList.remove("active");
                }
                oBtn.className = "active";
                this.contentBoxs[oBtn.index].classList.add("active");
            }
        }
    </script>
</head>

<body>
    <div id="box">
        <div class="btnGrop">
            <button class="active">前端工程师</button>
            <button>后端工程师</button>
            <button>UI设计师</button>
        </div>
        <div class="contentBox active">前端工程师是互联网时代软件产品研发中不可缺少的一种专业研发角色。从狭义上讲,前端工程师使用 HTML、CSS、JavaScript 等专业技能和工具将产品UI设计稿实现成网站产品,涵盖用户PC端、移动端网页,处理视觉和交互问题。从广义上来讲,所有用户终端产品与视觉和交互有关的部分,都是前端工程师的专业领域。</div>
        <div class="contentBox">后端工程师隶属于软件研发工程师,是从事软件开发相关工作人员,其主要职责是平台设计、接口设计和功能实现。</div>
        <div class="contentBox">“UI”的本义是用户界面,是英文User和interface的缩写。UI设计师简称UID(User Interface Designer),指从事对软件的人机交互、操作逻辑、界面美观的整体设计工作的人。<br> UI设计师的涉及范围包括高级网页设计、移动应用界面设计,是目前中国信息产业中最为抢手的人才之一。
        </div>
    </div>
    <div id="box2">
        <div class="btnGrop">
            <button class="active">前端工程师</button>
            <button>后端工程师</button>
            <button>UI设计师</button>
        </div>
        <div class="contentBox active">前端工程师是互联网时代软件产品研发中不可缺少的一种专业研发角色。从狭义上讲,前端工程师使用 HTML、CSS、JavaScript 等专业技能和工具将产品UI设计稿实现成网站产品,涵盖用户PC端、移动端网页,处理视觉和交互问题。从广义上来讲,所有用户终端产品与视觉和交互有关的部分,都是前端工程师的专业领域。</div>
        <div class="contentBox">后端工程师隶属于软件研发工程师,是从事软件开发相关工作人员,其主要职责是平台设计、接口设计和功能实现。</div>
        <div class="contentBox">“UI”的本义是用户界面,是英文User和interface的缩写。UI设计师简称UID(User Interface Designer),指从事对软件的人机交互、操作逻辑、界面美观的整体设计工作的人。<br> UI设计师的涉及范围包括高级网页设计、移动应用界面设计,是目前中国信息产业中最为抢手的人才之一。
        </div>
    </div>
</body>

</html>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值