面向对象👀
🌈认识对象
对象是”键值对“的集合,表示属性和值的映射关系
1 2 3 4 5 6
| var obj = { name: '小明', age: 19, gender: '男', hobbies: ['羽毛球', '乒乓球'] }
|
❗注意及规范
对象的语法:键和值之间用冒号隔开,每组键值对之间用逗号隔开,最后一个键值对后可以不写逗号
属性是否加引号:如果对象的属性键名不符合JS标识符命名规范,则这个键名必须用引号包裹,如对象键值对: ‘favorite-book’: ‘舒克和贝塔’
📍属性的访问
1 2 3 4 5 6 7 8 9 10 11
| obj.name; obj.age; obj.hobbies;
obj['favorite-book'];
var key = 'gender' obj.key; obj[key];
|
📍属性的更改
1 2 3 4 5 6 7
| var obj = { a: 10 } obj.a = 20; obj.a++; console.log(obj.a);
|
📍属性的创建
1 2 3 4 5 6
| var obj = { a: 10 } obj.b = 20 console.log(obj);
|
📍属性的删除
1 2 3 4 5 6 7
| var obj = { a: 10, b: 20 } delete obj.a console.log(obj);
|
⭕对象的方法
如果某个属性值是函数,则它也被称为对象的“方法”
方法也是函数,只不过方法是对象的“函数属性”,它需要对象打点调用
1 2 3 4 5 6 7 8 9 10
| var obj = { name: '小明', age: 19, gender: '男', hobbies: ['羽毛球', '乒乓球'], sayHello: function(){ console.log('你好') } }
|
📍方法的调用
⭕对象的遍历
使用 for…in…循环可以遍历对象的每个键
for (var key in obj) {
console.log(‘属性名’ + key , ‘属性值’ + obj[key])
}
💡对象的浅克隆
对象是引用类型值
1.不能用 var obj2 = obj1 这样的语法克隆一个对象
2.使用 == 或者 === 进行对象的比较时,比较的是它们是否为内存中的同一个对象,而不是比较值是否相同
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| <!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> var obj1 = { a: 1, b: 2, c: [3, 4, 5, [66, 77]], sayHello: function () { console.log('你好'); }, };
function clone(obj) { var result = {}; for (var key in obj) { result[key] = obj[key]; } return result; }
var obj2 = clone(obj1); obj1.a++; console.log(obj2.a); console.log(obj2.sayHello == obj1.sayHello); console.log(obj2 == obj1); </script> </body> </html>
|
💡对象的深克隆
❗注意点:使用递归来进行对象的深克隆,当属性值为引用类型值时,应该优先判断是否是数组,然后再判断是否为对象,因为数组的typeof Array 结果为 object ,所以应先判断是否为数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| <!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> var obj1 = { a: 1, b: 2, c: [3, 4, 5, [66, 77], { c: 88, d: 99 }], };
function deepClone(obj) { if (Array.isArray(obj)) { var result = []; for (var i = 0; i < obj.length; i++) { result.push(deepClone(obj[i])); } } else if (typeof obj == 'object') { var result = {}; for (var key in obj) { result[key] = deepClone(obj[key]); } } else { var result = obj; } return result; }
var obj2 = deepClone(obj1); console.log(obj2 == obj1); console.log(obj2.c == obj1.c); </script> </body> </html>
|
🌈上下文规则
📍函数的上下文
函数中可以使用 this 关键字,它表示函数的上下文
与中文中“这”类似,函数中的this具体指代什么必须通过调用函数的“前言后语”来判断
📍函数中的this
1 2 3 4 5 6 7 8 9 10 11 12 13
| var obj = { name: '小明', age: 19, gender: '男', hobbies: ['羽毛球', '乒乓球'], sayHello: function () { console.log('我是'+ this.name, '今年' + this.age + '岁了') } }; obj.sayHello()
var sayHello = obj.sayHello; sayHello();
|
💡函数的上下文由调用方式决定
同一个函数,用不同的形式调用它,则函数的上下文不同
1 2 3 4 5
| obj.sayHello();
var sayHello = obj.sayHello; sayHello();
|
⭕上下文规则1~6
📍函数的上下文由调用函数的方式来决定
函数的上下文(this关键字)由调用函数的方式决定,function是“运行时上下文”策略
函数如果不调用,则不能确定函数的上下文
💡规则1
对象打点调用它的方法函数,则函数的上下文是这个打点的对象
即: 对象.方法()
💡规则2
圆括号直接调用函数,则函数的上下文是window对象
即:函数()
💡规则3
数组(类数组对象)枚举出函数进行调用,上下文是这个数组(类数组对象)
即:数组[下标] ()
💡规则4
IIFE中的函数,上下文是window对象
即:( function () {函数体} )();
💡规则5
定时器,延时器调用函数,上下文是window对象
即:setInterval(函数, 时间); 、setTimeout(函数, 时间);
💡规则6
事件处理函数的上下文是绑定事件的DOM元素
即:元素.addEeventListener(事件类型, function() {函数体})
🔔规则6 — 小案例1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| <!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> div { float: left; margin-right: 20px; width: 200px; height: 200px; border: 1px solid #000; } </style> </head> <body> <div id="box1"></div> <div id="box2"></div> <div id="box3"></div> <script> var oBox1 = document.getElementById('box1') var oBox2 = document.getElementById('box2') var oBox3 = document.getElementById('box3') function changeColor () { this.style.backgroundColor = 'red' } oBox1.addEventListener('click', changeColor) oBox2.addEventListener('click', changeColor) oBox3.addEventListener('click', changeColor) </script> </body> </html>
|
🔔规则6 — 小案例2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| <!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> div { float: left; margin-right: 20px; width: 200px; height: 200px; border: 1px solid #000; } </style> </head> <body> <div id="box1"></div> <div id="box2"></div> <div id="box3"></div> <script> var oBox1 = document.getElementById('box1') var oBox2 = document.getElementById('box2') var oBox3 = document.getElementById('box3') function changeColor () { var self = this setTimeout(function(){ self.style.backgroundColor = 'red' },2000) } oBox1.addEventListener('click', changeColor) oBox2.addEventListener('click', changeColor) oBox3.addEventListener('click', changeColor) </script> </body> </html>
|
⭕call和apply能指定函数的上下文
函数.call(上下文);
函数.apply(上下文);
📍call和apply的区别
call传参时要用逗号罗列参数
apply传参时要把参数写到数组中
🔔上下文规则总结
| 规则 |
上下文 |
| 对象.函数() |
对象 |
| 函数() |
window |
| 数组[下标] () |
数组 |
| IIFE |
window |
| 定时器、延时器 |
window |
| DOM事件处理函数 |
绑定DOM的元素 |
| call和apply |
任意指定 |
| 用new调用函数 |
秘密创建出的对象 |
⭕构造函数与类
📍用new操作符调用函数
new 函数()
JS规定,使用new操作符调用函数会进行“四步走”:
- 函数体内会自动创建出一个空白对象
- 函数的上下文(this)会指向这个对象
- 函数体内的语句会执行
- 函数会自动返回上下文对象,即使函数没有return语句
1 2 3 4 5 6 7
| function fun () { this.a = 3; this.b = 5; } var obj = new fun(); console.log(obj);
|
⭕构造函数
用new调用一个函数,这个函数就被称为“构造函数”,任何函数都可以是构造函数,只需要用new调用它
顾名思义,构造函数用来“构造新对象”,它内部的语句将为新对象添加若干属性和方法,完成对象的初始化
构造函数必须用new关键字调用,否则不能正常工作,正因如此,开发者约定构造函数命名时首字母要大写
❗构造函数中的this不是函数本身
1 2 3 4 5 6 7 8 9 10
| function People(naem, age, gender) { this.name = name; this.age = age; this.gender = gender; this.sayHello = function () { console.log('我是' + this.name + ',我今年' + this.age + '岁了'); } } var xiaoming = new People('小明',18,'男'); var xiaomei = new People('小美',22,'女');
|
⭕原型原型链
📍什么是prototype
任何函数都有prototype属性,prototype是原型的意思
prototype属性值是个对象,它默认拥有constructor属性指回函数
普通函数来说的prototype属性没有任何用处,而构造函数的prototype属性非常有用
构造函数的prototype属性是它的实例的原型
💡原型链查找
JS规定:实例可以打点访问他的原型的属性和方法,这被称为“原型链查找”
1 2 3 4 5 6 7 8 9 10 11 12 13
| function People(naem, age, gender) { this.name = name; this.age = age; this.gender = gender; } People.prototype.hobbies = ['挣钱','敲代码'];
var xiaoming = new People('小明', 19, '男'); console.log(xiaoming.hobbies );
var tom = new People('Tom', 20, '男'); tom.hobbies = ['吃饭', '睡觉'] console.log(tom.hobbies);
|
💡hasOwnProperty方法
hasOwnProperty方法可以检查对象是否真正“自己拥有”某属性或者方法
1 2 3 4
| xiaoming.hasOwnPropety('name'); xiaoming.hasOwnProperty('age'); xiaoming.hasOwnProperty('gender'); xiaoming.hasOwnProperty('hobbies');
|
💡in运算符
in运算符只能检查好某个属性或者方法是否可以被对象访问,不能检查是否是自己的属性或方法
1 2 3 4
| 'name' in xiaoming; 'age' in xiaoming; 'gender' in xiaoming; 'hobbies' in xiaoming;
|
📍在prototype上添加方法
为什么要这样做:把方法直接添加到实例身上的缺点,每个实例和每个实例的方法函数都是内存中不同的函数,造成了内存的浪费
解决办法:将方法写到prototype上
1 2 3 4 5 6 7 8 9 10 11 12
| function People(naem, age, gender) { this.name = name; this.age = age; this.gender = gender; }
People.prototype.sayHello = function(){ console.log('我是' + this.name + ',我今年' + this.age + '岁了'); }
var xiaoming = new People('小明', 19, '男'); xiaoming.sayHello();
|
📍原型链的终点
📍JS中如何实现继承
实现继承的关键在于:子类必须拥有父类的全部属性和方法,同时子类还应该能定义自己特有的属性和方法
使用JS特有的原型链特性来实现继承,是普遍的做法
📍通过原型链实现继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| <!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 Person (name, age, sex){ this.name = name this.age = age this.sex = sex }
Person.prototype.sayHello = function() { console.log('我是' + this.name + ',我今年' + this.age + '岁了,我是一个' + this.sex + '生!') } Person.prototype.sleep = function () { console.log(this.name + '开始睡觉,zzzzzz') } function Student (name,age,sex,school,studentId){ this.name = name this.age = age this.sex = sex this.school = school this.studentId = studentId } Student.prototype = new Person()
Student.prototype.study = function () { console.log(this.name + '正在学习') } Student.prototype.exam = function () { console.log(this.name + '正在考试,加油') }
Student.prototype.sayHello = function (){ console.log('敬礼!我是' + this.name + ',我今年' + this.age + '岁了,我是一个' + this.sex + '生!') }
var hanmeimei = new Student('韩梅梅', 18, '女') hanmeimei.study() hanmeimei.sayHello()
var laowang = new Person('老王', 73, '男') laowang.sayHello() </script> </body> </html>
|
💡上升到面向对象—红绿灯小案例
页面上做一个红绿灯,点击红灯就变黄,点击黄灯就变绿,点击绿灯就变红,同时,页面上还要添加100个红绿灯
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| <!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> img { width: 50px; } </style> </head> <body> <script> var color = ['红灯', '黄灯', '绿灯']; function TrafficLight() { this.color = 0;
this.init(); this.bindEvent() }
TrafficLight.prototype.init = function () { this.dom = document.createElement('img'); this.dom.src = 'https://c53d203.webp.li/'+ color[this.color] +'.png' document.body.append(this.dom) };
TrafficLight.prototype.changeColor = function () { this.color++ if (this.color >= color.length) { this.color = 0 } this.dom.src = 'https://c53d203.webp.li/'+ color[this.color] +'.png' }
TrafficLight.prototype.bindEvent = function(){ var self = this this.dom.addEventListener('click',function(){ self.changeColor() }) }
var n = 100 while(n--){ new TrafficLight() } </script> </body> </html>
|
💡上升到面向对象—炫彩小球案例
页面实现炫彩小球,移动鼠标,会有很多不同颜色的小球出现,慢慢变大消失
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
| <!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> .ball { position: absolute; border-radius: 50%; } </style> </head> <body> <div class="ball"></div> <script> function Ball(x, y) { this.x = x; this.y = y; this.opacity = 1; do { this.dx = parseInt(Math.random() * 16) - 8; this.dy = parseInt(Math.random() * 16) - 8; } while (this.dx == 0 && this.dy == 0);
this.r = 10; this.color = colorArr[parseInt(Math.random() * colorArr.length)];
this.init();
ballArr.push(this); }
Ball.prototype.init = function () { this.dom = document.createElement('div'); this.dom.classList.add('ball'); this.dom.style.width = 2 * this.r + 'px'; this.dom.style.height = 2 * this.r + 'px'; this.dom.style.left = this.x - this.r + 'px'; this.dom.style.top = this.y - this.r + 'px'; this.dom.style.backgroundColor = this.color; this.dom.style.opacity = this.opacity document.body.append(this.dom); };
Ball.prototype.update = function () { this.x += this.dx; this.y += this.dy; this.r += 0.08 this.opacity -= 0.02
this.dom.classList.add('ball'); this.dom.style.width = 2 * this.r + 'px'; this.dom.style.height = 2 * this.r + 'px'; this.dom.style.left = this.x - this.r + 'px'; this.dom.style.top = this.y - this.r + 'px'; this.dom.style.backgroundColor = this.color; this.dom.style.opacity = this.opacity };
var ballArr = []; var colorArr = ['#6CC', '#cf6', '#f9c', '#f66', '#c39', '#f60'];
setInterval(function () { for (var i = 0; i < ballArr.length; i++) { ballArr[i].update(); } }, 20);
window.addEventListener('mousemove', function (e) { new Ball(e.clientX, e.clientY); }); </script> </body> </html>
|
⭕内置对象
📍包装类
Number()、String()、Boolean()分别是数字、字符串、布尔值的“包装类”
Number()、String()、Boolean()的实例都是object类型,他们的PrimitiveValue属性存储它们的本身值
new出来的基本类型值可以正常参与运算
很多编程语言都有“包装类”的设计,包装类的目的就是为了让基本数据类型可以从它们的构造函数的prototype上获得方法
⭕Math数学对象
| 方法 |
描述 |
| Math.pow() |
幂 |
| Math.sqrt() |
开方 |
| Math.floor() |
向下取整 |
| Math.ceil() |
向上取整 |
| Math.round() |
四舍五入 |
| Math.max() |
得到参数列表的最大值 |
| Math.min() |
得到参数列表的最小值 |
| Math.random() |
得到0~1之间的小数,区间为[0,1);得到a,b区间的公式:parseInt(Math.random() *(b-a+1) )+ a |
📍四舍五入到小数点后某位
数字乘100 后四舍五入 再除以100
其实,可以使用 数字.toFixed(要保留的位数) 完成保留几位小数并会四舍五入
⭕日期对象
使用new Date()即可得到当前时间的日期对象,它是object类型值
使用new Date(2024, 10, 1)即可得到指定日期的日期对象,注意第二个参数表示月份,从0开始算,10表示11月
也可以使用这种写法: new Date(‘2024-11-01’)这样的写法,注意:月份日期必须是两位,不足要补零
🔔日期对象常见方法
| 方法 |
功能 |
| getDate() |
得到日期1~31 |
| getDay() |
得到星期0~6 |
| getMonth() |
得到月份0~11 |
| getFullYear() |
得到年份 |
| getHours() |
得到消失0~23 |
| getMinutes() |
得到分钟0~59 |
| getSeconds() |
得到秒数0~59 |
💡时间戳
时间戳表示1970年1月1日零点距离某时刻的毫秒数
通过 日期对象.getTime() 方法或者Date.parse(日期对象)函数可以将日期对象变为时间戳
通过new Date(时间戳)的写法,可以将时间戳变为日期对象
💡小案例:倒计时小程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| <!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> <h1>距离2025年高考还有</h1> <h2 id="time"></h2> <script> var oH2 = document.getElementById('time'); var now, exam, time, day, hours, minutes, seconds;
function render() { now = new Date(); now = Date.parse(now); exam = new Date(2025, 5, 7); exam = Date.parse(exam); time = exam - now; day = parseInt(time / (1000 * 60 * 60 * 24)); hours = parseInt((time / (1000 * 60 * 60)) % 24); minutes = parseInt((time / (1000 * 60)) % 60); seconds = parseInt((time / 1000) % 60); oH2.innerText = day + '天' + hours + '小时' + minutes + '分钟' + seconds + '秒'; }
render() setInterval(render, 1000); </script> </body> </html>
|
💡[拓展]继承与内置构造函数
📍借用构造函数
为了解决原型中包含引用类型值所带来问题和子类构造函数不优雅的问题,通常使用“借助构造函数”的技术,也被称为“伪造对象”或“经典继承”
借助构造函数的思想非常简单:在子类构造函数的内部调用超类的构造函数,但是要注意使用call() 绑定上下文
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| function People(name, age, gender) { this.name = name; this.age = age; this.gender = gender; }
function Student(name, age, gender, stuId, school) { People.call(this, name, age, gender); this.stuId = stuId; this.school = school; }
var xiaoming = new Student('小明', 19, '男', 233, '中心小学'); console.log(xiaoming)
|
📍组合继承
将借用原型链和借用构造函数的技术组合到一起,叫做组合继承,也叫作伪经典继承
组合继承是JS中最常用的继承模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| function People(name, age, gender) { this.name = name; this.age = age; this.gender = gender; }
People.prototype.sayHello = function () { console.log('我是' + this.name + ',我今年' + this.age + '岁了,我是' + this.sex + '生'); }; People.prototype.sleep = function () { console.log(this.name + '正在睡觉'); };
function Student(name, age, gender, stuId, school) { People.call(this, name, age, gender); this.stuId = stuId; this.school = school; }
Student.prototype = new People();
Student.prototype.study = function () { console.log(this.name + '正在学习'); };
Student.prototype.sayHello = function () { console.log('敬礼!我是' + this.name + ',我今年' + this.age + '岁了,我是' + this.sex + '生'); };
var xiaoming = new Student('小明', 19, '男', 233, '中心小学'); console.log(xiaoming); xiaoming.sleep(); xiaoming.sayHello();
|
❗组合继承的缺点
组合继承最大的问题就是无论什么情况下,都会调用两次超类的构造函数:一次是在创建子类原型的时候,另一次是在子类构造函数的内部
📍原型式继承
Object.create() 方法,可以根据指定的对象为原型创建出新的对象
var obj2 = Object.create(obj1);
即:obj2.proto_ = obj1
在没有必要“兴师动众”地创建构造函数,而只是想让新对象与现有对象“类似”的情况下,使用Object.create()即可胜任,成为原型式继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| <!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> var obj1 = { a: 1, b: 2, fun: function () { console.log('结果为:' + (this.a + this.b)); }, };
var obj2 = Object.create(obj1); console.log(obj2.a); console.log(obj2.b); obj2.fun(); console.log(obj1 === obj2); console.log(obj1.a === obj2.a); console.log(obj1.fun === obj2.fun); console.log(obj2.hasOwnProperty('a'));
var obj3 = Object.create(obj1, { a: { value: 5, }, b: { value: 15, }, fun: { value: function () { console.log('相加得' + (this.a + this.b)); }, }, }); console.log(obj3.a); console.log(obj1.a); obj3.fun(); </script> </body> </html>
|
📍寄生式继承
寄生式继承:编写一个函数,它接收一个参数o,返回以o为原型的新对象p,同时给p上添加预置的新方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| <!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 fun(o) { var p = Object.create(o); p.sayHello = function () { console.log('你好'); }; return p; }
var obj1 = { a: 1, b: 2, fun: function () { console.log('结果' + (this.a + this.b)); }, };
var obj2 = fun(obj1); console.log(obj2 === obj1); console.log(obj2.a === obj1.a); obj2.fun(); obj2.sayHello(); </script> </body> </html>
|
📍寄生组合式继承
寄生组合式继承:通过借用构造函数来继承属性,通过原型链的混成形式来继承方法
基本思路:不必为了指定子类型的原型而调用超类的构造函数,我们所需要的无非就是超类原型的一个副本而已。本质上,就是使用寄生式继承来继承超类的原型,然后在将结果指定给子类的原型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| <!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 inheritPrototype(subType, superType){ var prototype = Object.create(superType.prototype) subType.prototype = prototype }
function People(name, age, sex) { this.name = name; this.age = age; this.sex = sex; } People.prototype.sayHello = function () { console.log('我是' + this.name + ',我今年' + this.age + '岁了,我是' + this.sex + '生'); }; People.prototype.sleep = function () { console.log(this.name + '正在睡觉'); };
function Student(name, age, sex, stuId, school) { People.call(this, name, age, sex); this.stuId = stuId; this.school = school; }
inheritPrototype(Student,People)
Student.prototype.study = function () { console.log(this.name + '正在学习'); }; Student.prototype.sayHello = function () { console.log('敬礼!我是' + this.name + ',我今年' + this.age + '岁了,我是' + this.sex + '生'); };
var xiaoming = new Student('小明', 12, '男') xiaoming.sayHello() xiaoming.study() xiaoming.sleep() </script> </body> </html>
|
💡instanceof运算符
instanceof运算符用来检测“某对象是不是某个类的实例”,比如:xiaoming instanceof Student
底层机理:检查Student.prototype属性是否在xiaoming的原型链上(多少层都行,只要在就行)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
function People() {}
function Student() {}
Student.prototype = new People();
var stu = new Student();
console.log(stu instanceof Student); console.log(stu instanceof People);
|
💡内置构造函数
JS有很多内置构造函数,比如Array就是数组类型的构造函数,Function就是函数类型的构造函数,Object就是对象类型的构造函数
内置构造函数非常有用,所有该类型的方法都是定义在它的内置构造函数的prototype上的,我们可以给这个对象添加新的方法,从而拓展某类型的功能
💡Object.prototype是万物原型链的终点
Object.prototype是万物原型链的终点。JS中函数、数组皆为对象,以数组为例,完整的原型链是这样的:
💡内置构造函数的关系
任何函数都可以看做是Function “new出来的” , 那么Object也是函数,当然也是Function “new出来的”