2021-02-26构造函数
文章目录
- 构造函数
- 1.什么是构造函数
- 2.为什么要使用构造函数
- 如何封装一个构造函数
- 3.构造函数的执行过程
- 4.构造函数的返回值
- 5.与普通函数的区别
- 5.1调用方式的不同
- 5.2 返回值不同
- 5.3 作用的不同
- 原型对象
- 对象的封装
- 混合模式(构造函数+原型 模式)
- `__proto__`
- 原型链
- 继承
- 使用call()方法实现继承
- 特点:
- 使用prototype实现继承
- 组合式继承
- 多态
- ES6面向对象
- class 类
- ES6继承
- super
- 封装拖拽
- 拖拽1.0
- 拖拽2.0
- 拖拽3.0
- 封装拖拽
- this指向
- 面向对象选项卡
构造函数
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个步骤。
- 当用
new
关键字调用构造函数时,会开辟一个新的内存空间,这个内容空间就是新的对象实例。 - 将构造函数内部的
this
指向当前的内存空间(新的对象)。 - 执行函数内的代码。(对象的属性和方法的赋值。)
- 将内存空间的地址作为返回值返回。
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); //trueconsole.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>
2021-02-26构造函数相关推荐
- Leetcode刷题 2021.02.26
Leetcode刷题 2021.02.26 Leetcode1178 猜字谜 Leetcode869 重新排序得到 2 的幂 Leetcode1676 二叉树的最近公共祖先 IV Leetcode11 ...
- java 流程控制篇 2021/02/26持续更新中
1. 用户交互Scanner 1.1 简单的Scanner用法 首先,需要 import java.util.Scanner 其次,需要创建一个 Scanner 类的对象, Scanner s = n ...
- 通关4级之词汇(2021.02.26)
2020.12.6 前言 这篇词汇是通关4级系列的最后一篇文章了,完结撒花★,°:.☆( ̄▽ ̄)/$:.°★ 更多相关文章点击阅读 通关4级之阅读理解 通关4级之听力 通关4级之写作 通关4级之翻译 ...
- 通关4级之听力(2021.02.26)
前言 通关4级系列已经完结了★,°:.☆( ̄▽ ̄)/$:.°★ 更多相关文章点击阅读 通关4级之阅读理解 通关4级之听力 通关4级之写作 通关4级之翻译 通关4级之词汇 点赞和评论超过20,橙子会把2 ...
- 通关4级之翻译(2021.02.26)
前言 通关4级系列已经完结了★,°:.☆( ̄▽ ̄)/$:.°★ 更多相关文章点击阅读 通关4级之阅读理解 通关4级之听力 通关4级之写作 通关4级之翻译 通关4级之词汇 点赞和评论超过20,橙子会把2 ...
- 字符环 2021.02.26
题目描述 有两个由字符构成的环.请写一个程序,计算这两个字符环上最长连续公共字符串的长度.例如,字符串"ABCEFAGADEGKABUVKLM"的首尾连在一起,构成一个环:字符串& ...
- 前端面试题笔记 2021.8.26
2021.8.26学习笔记 如果需要匹配包含文本的元素,用下面哪种方法来实现? A. text() B. contains() C. input() D. attr(name) 正确答案: B tex ...
- Java入门篇 2021/02/22
Java入门篇 2021/02/22 Java的三大版本 java三大版本主要包括 JavaSE, JavaME, JavaEE,其中现如今的JavaME已经是很少见了,JavaSE是JavaEE的基 ...
- 【386天】跃迁之路——程序员高效学习方法论探索系列(实验阶段143-2018.02.26)...
@(一只心中无码的程序员)专栏 实验说明 从2017.10.6起,开启这个系列,目标只有一个:通过探索新的学习方法,用2年的时间,实现2.5倍速的成长,获得普通程序员>= 5年的技术水平. 实验 ...
- 编译小米2s CyanogenMod 版本遇到的几个问题 (02.26更新)
背景介绍 为了破除我对android的神秘感,准备亲自编一个cm rom到真机上跑.反复看了cm 支持的devices, 我能网上,国内买到最便宜的,就只有小米2s了[0],为此特意从小米官网定了一个 ...
最新文章
- 内联函数与普通函数、成员函数与静态函数的区别(知识整理)
- Configured broker.id 2 doesn‘t match stored broker.id 3 in meta.properties
- Exception in thread “main“ java.io.FileNotFoundException: C:\Temp (拒绝访问。)
- 102_Power Pivot DAX 排名后加上总排名数
- centos6.6-zabbix2.4.5安装实战
- day25-python之继承组合
- Bing搜索背景图抓取
- 德芙网络营销策略ppt_看德芙网络营销策划经典案例,戳这里!
- ussd代码大全_USSD查询器app
- 关于阿狸狗破戒大师自动安装完cadence17.4后licence不可用问题
- C# Resharper的简单使用介绍
- Nginx反向代理负载均衡群集实战
- 计算思维是运用计算机科学的什么进行,什么是计算思维?
- 编写MTK6737平台的GPIO驱动例程(六)
- Intel 至强E5/E7 V4 CPU与至强可扩展CPU性能对比表
- 详细设计和概要设计的思考
- js适配IOS代T时间戳转日期
- 软件架构设计系列总结
- 一套java vue 开发的ERP源代码
- 小程序制作二维码和条形码