JS面向对象到底有啥用?面向对象里函数的写法和普通函数写法有啥区别?都有哪些优势?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JS面向对象到底有啥用?面向对象里函数的写法和普通函数写法有啥区别?都有哪些优势?相关的知识,希望对你有一定的参考价值。
既然推介到我这里,我也讲一下我的理解吧。js是一门基于对象和事件驱动的解释性脚本语言,具有与Java和C语言类似的语法,由浏览器解析执行。而由于内置了大量的对象,所以面向对象编程更有利于程序代码的复用性,扩展性,和封装性!函数的写法基本上是一样的。优势就是模块化,更利于维护和封装,重用等! 参考技术A 代码复用性,可维护性,可扩展性,封装性比较好本回答被提问者采纳
js构造函数--面向对象
面向对象: 在程序中,先用对象集中保存属性和方法。然后,再按需使用保存好的属性和方法。(细品后文,了解面向对象思想)
三大特点: 封装、继承、多态
封装
- 第一种方式 { } 创建对象
写法:
var 对象名={
属性名: 属性值,
属性名: 属性值,
… : …
方法名: function(){ … }
}
函数 和 方法 区别:
1). 相同点: 本质是完全一样的!都是function(){ … }
2). 不同点:
i. 函数: 特指不包含在任何对象中,可独立使用的function
ii. 方法: 特指包含在对象中,必须先找到对象,才能调用的function
如何访问对象中的属性和方法:
1). 访问对象中的属性: 必须先找到对象,再用.操作符,进入对象中,使用对象的属性
对象.属性
2). 访问对象中的方法: 必须先找到对象,再用.操作符,进入对象中,使用对象中的方法。但是必须加()调用
对象.方法名()
示例: 使用 { } 创建对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>对象第一种方式</title>
</head>
<body>
<script>
// 创建一个对象,保存一个学生lilei的个人信息属性和自我介绍方法
let lilei = {
sname:"Li Lei",
sage:11,
intr:function(){
console.log(this); // {sname: "Li Lei", sage: 11, intr: ƒ}
console.log(`I'm ${this.sname}, I'm ${this.sage}`)
}
};
console.log(lilei); // {sname: "Li Lei", sage: 11, intr: ƒ}
console.log(lilei.sage); // 11 输出lilei的年龄
lilei.intr(); // // I'm Li Lei, I'm 11 请lilei做自我介绍
lilei.sage++; // 过了一年,lilei长了一岁
console.log(lilei.sage);// 12 再输出lilei的年龄
lilei.intr(); // I'm Li Lei, I'm 12 再请lilei做自我介绍
console.log(lilei); // {sname: "Li Lei", sage: 12, intr: ƒ}
</script>
</body>
</html>
结果
- 第二种方式 用new来创建对象
用new来创建:
如何: 2步:
1. 先创建一个空对象:var 对象名=new Object()
2. 再强行向空对象中添加新属性和新方法
对象名.新属性=值
对象名.新属性=function(){ … this.属性名 … }
揭示: js中所有对象底层其实都是关联数组:
a. 对象和关联数组都是名值对儿的组合。
b. 无论访问数组的元素,还是访问对象中的属性,都有两种方式:
1). 标准写法: 数组名/对象名[“下标名或属性名”]
2). 简写: 数组名/对象名.下标明或属性名
3). 特例: 如果要访问的下标名或属性名不是写死的,来自于一个动态变化的变量,则既不能用.,也不能用[""],只能用[变量]
c. 访问数组或对象中不存在的成员,不会报错,而是返回undefined。
固定用法: 如何判断一个对象是否包含某个成员: 强行访问
数组或对象.下标名或属性名 !== undefined 说明包含该成员
数组或对象.下标名或属性名 === undefined 说明不包含该成员
d. 强行给数组或对象中不存在的位置赋值,不会报错,而是自动为数组或对象添加该新的成员。
固定用法: 如何给数组或对象中添加一个新成员: 强行赋值!
数组或对象.下标名或属性名=新值!
e. 都可以用for in循环遍历每个成员
//in会依次取出数组或对象中每个下标名或属性名
//保存在in前的变量中
for(var 变量 in 数组或对象){
//可以通过"数组或对象[变量]"方式来访问数组或对象中每个成员
}
示例: 使用new Object()创建对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>对象第二种方式</title>
</head>
<body>
<script>
// 用new Object()创建一个对象,保存一个学生lilei的个人信息属性和自我介绍方法
let lilei = new Object();
lilei.sname = "Li Lei";
lilei["sage"] = 11;
lilei.intr = function(){
console.log(`I'm ${this.sname}, I'm ${this.sage}`)
};
console.log(lilei); // {sname: "Li Lei", sage: 11, intr: ƒ}
console.log(lilei.className); // undefined 想获取李磊的className属性,没有不报错返回undefined
lilei.className = "初一2班"; // 为lilei强行添加className
console.log(lilei); // {sname: "Li Lei", sage: 11, className: "初一2班", intr: ƒ}
// 想遍历李磊的每个属性
for(let shuxingming in lilei){
//错误:in前的是一个变量,值随遍历不断变化,所以变量无论如何不能放在""中!
//console.log(lilei["shuxingming"]);
//错误: .是[""]的简写,在底层同样会被翻译为[""]原版。所以,错误原因和第一种情况是一样的!
//console.log(lilei.shuxingming);
//正确: 因为[]中需要一个字符串类型的下标名或属性名,而in前的变量,每次获得的刚好就是一个下标名或属性名的字符串。所以,刚好对象。
console.log(lilei[shuxingming]);
}
</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>构造函数</title>
</head>
<body>
<script>
// 创建学生类型的构造函数
function Student(sname, sage){
// 因为将来创建对象时,才能知道具体每个学生的姓名和年龄,而且,每个学生的姓名和年龄各不相同,
// 所以,定义构造函数时,暂时用变量占位。
// this是固定写法,只有前边加上this.的属性和方法,将来才能进入新对象中!反之,前边没有this.的,都无法进入将来的新对象中。
this.sname = sname; // 习惯上,变量名应该和属性名相同!
this.sage = sage;
//方法。这里只是演示,实际开发时方法写在原型对象prototype中
//因为将方法放在构造函数内,那么每创建一个新对象,都会重复创建相同方法的副本,导致浪费内存!
this.intr = function(){ // 学生的自我介绍方法。这里只是演示,实际开发时方法写在原型对象prototype中
console.log(`I'm ${this.sname}, I'm ${this.sage}`)
}
}
// 使用构造函数创建的都必须用new来调用
let lilei = new Student("Li Lei",11);
let hmm = new Student("Han Meimei",12);
console.log(lilei); // Student {sname: "Li Lei", sage: 11, intr: ƒ}
lilei.intr(); // I'm Li Lei, I'm 11
console.log(hmm); // Student {sname: "Han Meimei", sage: 12, intr: ƒ}
hmm.intr(); // I'm Han Meimei, I'm 12
</script>
</body>
</html>
结果
继承
- 问题: 如果将方法定义放在构造函数内,那么每创建一个新对象,都会重复创建相同方法的副本——浪费内存!
- 解决: 今后,只要多个子对象,都要使用一个共同的功能时,都要用继承方式来实现!
- 什么是继承: 父对象中的成员,子对象无需重复创建,就可直接使用!
- 如何: js中都是采用继承原型对象的方式来实现继承
(1). 什么是原型对象(prototype):每种类型中,替将来所有的子对象集中保存共有成员的父对象。
(2). 何时: 今后只要多个子对象都需要共用一个功能时,都要将这个功能添加到原型对象中一份即可!
(3). 结果: 只要保存在原型对象中的成员,所有子对象无需重复创建,就可直接使用
(4). 如何创建原型对象:
a. 原型对象不用自己创建!
b. 只要我们每定义一个构造函数妈妈,都会自动附赠我们一个空的原型对象爸爸。
(5). 如何访问原型对象:
a. 每个构造函数内都自带一个隐藏的prototype属性,指向当前构造函数配对的原型对象。
b. 构造函数.prototype
(6). 如何向原型对象中添加共有成员: 只能强行赋值:
构造函数.prototype.共有成员=新值或function(){ … }
从此!构造函数中就不应该再包含任何方法定义!
所有共有的方法,都要集中添加到原型对象中!- 何时继承:
(1). 不用手工设置继承!
(2). 当用new调用构造函数,创建该类型的新对象时,new的第二步会自动设置新对象的_ proto 隐藏属性指向构造函数的原型对象,形成继承关系!
(3). js规定,凡是从 proto 指出的引用关系,就是继承关系!
(4). 结果: 将来使用子对象访问一个成员时: 就近原则,先自己后原型
a. 都会先在子对象自己身体里找,如果找到要用的成员,就优先使用自己身体里的成员
b. 如果自己身体里没有要用的成员,则js引擎会自动延 proto _去父对象原型对象中查找父对象中保存的共有属性。如果在父对象中找到了想用的成员,则子对象一样可以直接用.访问!就和访问自己的成员一模一样!
示例: 使用构造函数继承
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>继承</title>
</head>
<body>
<script>
// 创建学生类型的构造函数
function Student (sname, sage) {
this.sname = sname;
this.sage = sage;
//从此构造函数中就不应该包含方法定义。
//所有共用的方法都要集中添加到原型对象中。
}
//在Student构造函数的原型对象中为将来所有子对象添加共有的自我介绍方法
Student.prototype.intr = function () {
console.log(`I'm ${this.sname}, I'm ${this.sage}`)
};
// 创建学生对象
let lilei = new Student("Li Lei",11);
let hmm = new Student("Han Meimei",12);
console.log(lilei); // Student {sname: "Li Lei", sage: 11}
lilei.intr(); // I'm Li Lei, I'm 11
console.log(hmm); // Student {sname: "Han Meimei", sage: 12}
hmm.intr(); // I'm Han Meimei, I'm 12
</script>
</body>
</html>
结果
打印结果 | 画图理解 |
---|---|
多态
- 什么是多态: 只要一个函数不同情况下表现出不同的状态。
- 包含2种情况:
(1). 重载(overload): 一件事,传入不同的参数,执行不同的逻辑
(2). 重写(override):- 什么是重写: 如果子对象觉得从父对象继承来的成员不好用!子对象就可以在自己内部创建一个和父对象成员同名的自有成员。
- 结果: 从此,这个子对象,优先使用自己的自有成员,不再使用父对象中不好用的共有成员!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
function Student (sname, sage) {
this.sname = sname;
this.sage = sage;
}
//想让Student家的孩子,也有好用的toString
Student.prototype.toString=function(){
return `sname:${this.sname}, sage:${this.sage}`
};
let lilei = new Student("Li Lei",11);
let arr = [1,2,3];
let now = new Date();
console.log( lilei.toString() ); // sname:Li Lei, sage:11
console.log( arr.toString() ); // 1,2,3
console.log( now.toString() ); // Thu May 06 2021 20:30:40 GMT+0800 (中国标准时间)
</script>
</body>
</html>
结果
以上是关于JS面向对象到底有啥用?面向对象里函数的写法和普通函数写法有啥区别?都有哪些优势?的主要内容,如果未能解决你的问题,请参考以下文章