两大编程思想
pop 面向过程
oop 面向对象
面向对象具有灵活、代码可复用,容易维护和开发的优点,更适合多人合作的大型软件项目。
面向对象的特性:
封装性
继承性
多态性
面向对象 (抽象的,具体的)
在JS中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如,字符串,数组,数值,函数等。
对象是由属性和方法组成的:
类抽象了对象的公共部分
创建类,必须使用new来对类进行实例化才能使用
类中有个很重要的函数constructor构造函数
//创建一个类
class Star{
constructor(uname,age){
this.uname=uname;
this.age=age;
}
//在类中添加方法
sing(song){
// console.log("我会唱歌")
console.log(this.uname+song)
}
//(1)在类里面的所有函数不需要写function
//(2)多个函数方法之间不需要添加逗号分隔
}
//利用类创建对象,也称对类进行实例化
var zly=new Star("赵丽颖",31);
var zs=new Star("某爽",28);
console.log(zly);
console.log(zs);
// (1)通过class对象创建类,类名习惯性首字母大写
// (2)类里面有个constructor函数,可以接受传递过来的参数,同时返回实例对象
// (3)constructor函数只要new生成实例时,就会自动调用这个函数,如果我们不写这个函数,类也会自动生成这个函数
// (4)生成实例的new不能省略
// (5)最后注意语法规范,创建类 类名后面不要加小括号,生成实例 类名后面加小括号,构造函数不需要加function
super不仅可以调用父类中的构造函数,也可以调用父类的普通函数
//继承中的属性或者方法的查找原则:就近原则
//1.继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行子类的
//2.继承中,如果子类里面没有,就去查找父类里面有没有这个方法,如果有,就去执行父类中的这个方法
class Father {
constructor(x, y) {
this.x = x;
this.y = y;
}
sum() {
console.log(this.x + this.y);
}
}
class Son extends Father {
constructor(x, y) {
//super必须在子类this之前调用
super(x, y)//调用父类中的构造函数
this.x = x
this.y = y
}
sub(){
console.log(this.x - this.y);
}
}
var son = new Son(5,3);
son.sub();
son.sum();
var that;
var _that;
class Star{
constructor(uname){
that=this
console.log(this)
this.uname=uname;
this.btn=document.querySelector("button");
this.btn.onclick=this.sing;//不要加小括号,加上后就立马执行了
}
//在类中添加方法
sing(){
//这个sing方法里的this指向的是btn这个按钮,因为这个按钮调用了这个函数
console.log(this)
console.log(this.uname)
}
dance(){
//这个dance里面的this 指向的是实例对象zly 因为zly调用了这个函数
_that=this
console.log(this)
}
}
var zly=new Star("赵丽颖")
zly.dance();
console.log(that===zly);
console.log(_that===zly);
let、const声明的会产生块作用域,var不会产生块作用域
不同代码块之间的变量无法互相访问
推荐使用let和const
script标签内和JS文件就是全局变量
1.为 window对象动态添加的属性默认也是全局的,不推荐!
⒉.函数中未使用任何关键字声明的变量为全局变量,不推荐!!!
3.尽可能少的声明全局变量,防止全局变量被污染
本质上是底层的变量查找机制
在函数被执行时,会优先在当前函数作用域中查找变量
如果当前作用域查找不到,则会依次逐级查找父级作用域直到全局作用域
总结:
1.嵌套关系的作用域串联起来形成了作用域链
2.相同作用域链中按着从小到大的规则查找变量
3.子作用域能够访问父作用域,父级作用域无法访问子级作用域
内存的生命周期:
说明:
**内存泄漏:**程序中分配的内存由于某种原因未释放或无法释放叫做内存泄漏
存在一个致命问题:嵌套引用(循环引用)
如果两个对象相互引用,尽管他们已不再使用,垃圾回收器不会进行回收,导致内存泄漏
标记清除法
核心思路:从根部扫描对象,能查找到的就是使用的,查找不到的就要回收
闭包的作用:封闭数据,实现数据私有,外部也可以访问函数内的变量。
闭包很有用,它允许将函数与其他操作的某些数据(环境)关联起来
闭包了能会引起内存泄漏的问题
闭包的基本格式:
function outer(){
let i =10;
function fn(){
console.log(i);
}
return fn;
}
const fun= outer();
fun()
//简约写法
function outer(){
let i =10;
return function(){
console.log(i);
}
}
const fun= outer();
fun()
//闭包的应用: 统计函数调用的次数
let i=0;
function fn(){
i++
console.log(`函数被调用${i}次`)
}
//值会有被篡改的风险
//采用闭包的形式 实现数据的私有
function count(){
let i=0;
function fn(){
i++
console.log(`函数被调用${i}次`);
}
return fn
}
const fun=count()//全局,不会被回收
把所有var声明的提升到当前作用域的最前面
只提升声明,不提升赋值 undefined
不建议使用var声明,let、const不存在变量提升
知道函数参数默认值、动态参数,剩余参数的使用细节,提升函数应用的灵活度,知道箭头函数的语法及普通函数的差异
//1.会把所有函数声明提升到当前作用域的最前面
//2.只提升函数声明,不提升函数调用
fn()
function fn(){
console.log("函数提升");
}
//函数表达式必须先声明和赋值,才能调用
//这样写是错误的。因为只提升函数声明,不提升函数赋值,会报错
fun()
var fun = function () {
console.log("函数表达式");
}
总结:
切入点:arguments
function getSum(){
//arguments 动态参数 只存在于函数里面
//是伪数组
console.log(arguments);
}
getSum(2,3,4)
function getSum(){
let sum=0;
for(let i=0;i<arguments.length;i++){
sum+=arguments[i]
}
console.log(sum)
}
getSum(2,3,4)
getSum(1,2,3,4)
getSum(2,2,3,4)
总结:
function getSum(...arr) {
console.log(arr);
}
//arr可以随便起名
getSum(2, 3)
getSum(1, 2, 3)
剩余参数允许我们将一个不定数量的参数表示为一个数组
剩余参数主要的使用场景:用于获取多余的实参
剩余参数和动态参数的区别:动态参数是伪数组,剩余参数是真数组
const arr=[1,2,3]
//展开运算符可以展开数组
console.log(...arr);
不会修改原数组
典型应用场景:求数组最大值(最小值)、合并数组
const arr=[1,2,3]
//展开运算符可以展开数组
// console.log(...arr);
console.log(Math.max(1,2,3));
console.log(Math.max(...arr));
//结果都为3
//实质上...arr===1,2,3
//合并数组
const arr1=[1,2,3]
const arr2=[4,5,6]
const arr=[...arr1,...arr2]
console.log(arr);
总结:
1.展开运算符的主要作用
可以把数组展开,可以利用求数组最大值以及合并数组等操作
2.展开运算符和剩余参数的区别
展开运算符主要是数组展开
剩余参数在函数内部使用
**目的:**引入箭头函数的目的是更简短的函数写法并且不绑定this,箭头函数的语法比函数表达式更简洁。
使用场景:箭头函数更适用于那些本来需要匿名函数的地方
箭头函数主要替代函数表达式
只有一个形参的时候,可以省略小括号
只有一行代码的时候,我们可以省略大括号
只有一行代码可以省略return
箭头函数可以直接返回一个对象
// //普通函数
// const fn = function () {
// console.log(1, 2, 3);
// }
// fn()
// //箭头函数主要替代函数表达式
// const fn = () => {
// console.log(1, 2, 3);
// }
// fn()
// //箭头函数传递参数
// const fn=(x)=>{
// console.log(x);
// }
// fn(1)
// //只有一个形参的时候,可以省略小括号
// const fn=x=>{
// console.log(x);
// }
// fn(1)
// //只有一行代码的时候,我们可以省略大括号
// const fn=x=>console.log(x);
// fn(1)
//只有一行代码可以省略return
// const fn=x=>x+x;
// console.log(fn(1));
//箭头函数可以直接返回一个对象
const fn = (uname) => ({ uname: uname })
fn("赵丽颖")
console.log(fn("赵丽颖"));
总结:
普通函数有arguments动态参数
箭头函数没有arguments动态参数,但是有剩余参数…args
//利用箭头函数来求和
getSum = (...arr) => {
let sum = 0;
for (let i = 0; i < arr.length; i++) {
sum += arr[i]
}
return sum;
}
console.log(getSum(1,2,3,4,5));
总结:箭头函数中没有arguments动态参数,可以使用剩余参数
在箭头函数出现之前,没一个新函数根据他是如何被调用的来定义这个函数的this值。
//this指向window
console.log(this);
//普通函数中this指向window(即函数的调用者)
function fn(){
console.log(this);
}
window.fn()
//对象方法里的this指向对象
const obj={
name:'andy',
sayHi:function (){
console.log(this);
}
}
obj.sayHi()
箭头函数不会创建自己的this,它只会从自己的作用域链的上一层沿用this
const obj={
name:'andy',
sayHi:()=>{
console.log(this);
}
}
obj.sayHi()
const obj ={
uname:"pink老师",
sayHi:function(){
console.log(this);
let i=10
const count =()=>{
console.log(this);
}
count()
}
}
obj.sayHi()
//this指向都是obj
总结:
箭头函数不会创建自己的this,它只会从自己的作用域链的上一层沿用this
DOM事件回调函数不推荐使用箭头函数,特别是需要用到this的时候
事件回调函数使用箭头函数时,this为全局的window
解构的语法及分类,使用解构简洁语法快速为变量赋值
数组解构是将数组的单元值快速批量赋值给一系列变量的简洁语法
//基本语法:
const arr=[100,60,80]
//数组解构 赋值
const [max,min,avg]=arr
//就等价于如下赋值
// const max =arr[0]
// const max =arr[1]
// const max =arr[2]
console.log(max);
console.log(min);
console.log(avg);
//典型应用 变量交互
let a=1
let b=2;//这里必须要加分号
[b,a]=[a,b]
console.log(a,b);
必须加分号的两种情况:
//1.立即执行函数
(function(){ })();
(function(){ })();
//2.使用数组的时候
const str='pink';//如果这里不加分号,就会报错'pink'[1,2,3].map is not a function
[1,2,3].map(function(item){
console.log(item);
})
总结:
1.数组解构赋值的作用是什么?
试将数组的单元值快速批量赋值给一系列变量的简洁语法
2.JS前面有哪两种情况是必须要加分号的
补充:
字符串拼接新思路:
利用map()和join()数组方法实现字符串拼接
map可以遍历数组处理数据,并且返回新的数组
map也称为映射。映射是个术语,指两个元素的 集之间元素相互“对应”的关系。
map重点在于有返回值(返回一个数组),forEach没有返回值
const arr =['red','blue','pink']
//数组map方法
const newArr=arr.map(function(ele,index){
console.log(ele);//数组元素
console.log(index);//索引号
return ele +"颜色"
})
console.log(newArr);
join方法
const arr =['red','blue','pink']
//数组map方法
const newArr=arr.map(function(ele,index){
console.log(ele);//数组元素
console.log(index);//索引号
return ele +"颜色"
})
console.log(newArr);
//数组join方法
//把数组中的而所有元素转换成一个字符串
console.log(newArr.join());//小括号为空,用逗号分割 red颜色,blue颜色,pink颜色
console.log(newArr.join(''));//小括号为空,无分割 red颜色blue颜色pink颜色
console.log(newArr.join('|'));//小括号为空,用|分割 red颜色|blue颜色|pink颜色
数组解构的细节
// const pc=['海尔','联想','小米','方正']
// const [hr,lx,mi,fz]=['海尔','联想','小米','方正']
// console.log(hr);
// console.log(lx);
// console.log(mi);
// console.log(fz);
// function getValue(){
// return [100,60]
// }
// const[max,min]= getValue()
// console.log(max,min);
//1.变量多,单元值少,undefined
// const [a,b,c,d]=[1,2,3]
// console.log(a);//1
// console.log(b);//2
// console.log(c);//3
// console.log(d);//undefined
//2.变量少,单元值多
// const [a,b]=[1,2,3]
// console.log(a);//1
// console.log(b);//2
//3.利用剩余参数 变量少,单元值多
// const [a,b,...c]=[1,2,3,4]
// console.log(a);//1
// console.log(b);//2
// console.log(c);//3,4
//4.防止undefined传递,设个默认参数
// const [a=0,b=0]=[1,2]
// console.log(a);//1
// console.log(b);//2
//5.按需导入赋值
// const [a,b,,d]=[1,2,3,4]
// console.log(a);//1
// console.log(b);//2
// console.log(d);//4
// //6.支持多维数组解构
// const arr = [1, 2, [3, 4]]
// console.log(arr[0]);
// console.log(arr[1]);
// console.log(arr[2]);
// console.log(arr[2][0]);
const [a, b, [c,d]] = [1, 2, [3, 4]]
console.log(a);
console.log(b);
console.log(c);
console.log(d);
总结:
变量的数量大于单元值数量时,多余的变量将会被赋为undefined
变量的数量小于单元值的数量时,可以通过剩余参数…获取剩余单元值,但只能置于最末位
对象解构是将对象属性和方法快速批量赋值给一系列变量的简洁语法
// //对象解构
const obj={
uname:'pink老师',
age:18
}
//解构的语法
// const {uname,age}={ uname:'pink老师', age:18}
//等价于const uname=obj.uname
//要求属性名和变量名必须一致
// console.log(username);
// console.log(age);
//1.对象解构的变量名 可以重新改名 旧变量名:新变量名
// const{uname:username,age}={ uname:'pink老师', age:18}
// console.log(username);
// console.log(age);
//2.解构数组对象
const pig=[
{
uname:'佩奇',
age:6
}
]
const [{uname,age}]=pig
console.log(uname);
console.log(age);对象解构
const obj={
uname:'pink老师',
age:18
}
//解构的语法
const {uname,age}={ uname:'pink老师', age:18}
//等价于const uname=obj.uname
//要求属性名和变量名必须一致
console.log(uname);
console.log(age);
多级对象解构
const pig={
uname:'佩奇',
family:{
mother:'猪妈妈',
father:'猪爸爸',
brother:'乔治'
},
age:6
}
//多级对象解构
const {uname,family:{mother,father,brother}}=pig
console.log(uname);
console.log(mother);
console.log(father);
console.log(brother);
多级数组对象解构
const pig=[
{
uname:'佩奇',
family:{
mother:'猪妈妈',
father:'猪爸爸',
brother:'乔治'
},
age:6
}
]
const [{uname,family:{mother,father,brother}}]=pig
console.log(uname);
console.log(mother);
console.log(father);
console.log(brother);
forEach()方法用于调用数组的每个元素,并将元素传递给回调函数
主要使用场景:遍历数组的每个元素
//forEach就是遍历 加强版的for循环
const arr=['red','green','pink']
const result=arr.forEach(function(item,index){
console.log(item);//数组元素 red green pink
console.log(index);//索引号
})
console.log(result);//打印结果为undefined
```## 深入对象
### 创建对象的三种方式
**利用对象字面量创建对象**
```js
const o ={
uname:'佩奇'
}
利用系统给定的函数new object创建对象
// const obj=new Object()
// console.log(obj);
// obj.name="pink老师"
const obj=new Object({uname:"pink老师"})
console.log(obj);
自定义构造函数创建(抽封一下)
function Pig(name, age, gender) {
this.name = name
this.age = age
this.gender = gender
}
//创建佩奇对象
const Peppa=new Pig('佩奇',6,'女')
const George=new Pig('佩奇',3,'男')
const Mum=new Pig('猪妈妈',30,'女')
const Dad=new Pig('猪爸爸',32,'男')
console.log(Peppa);
console.log(George);
console.log(Mum);
console.log(Dad);
构造函数是一种特殊的函数,主要用来初始化对象
构造函数在技术上是常规函数
不过有两个约定:
//创建一个猪 构造函数
function Pig(uname,age){
this.uname=uname //第一个uname属性//第二个uname形参
this.age=age
}
console.log( new Pig('佩奇',6));
说明:
总结:
构造函数的作用是什么?怎么写?
new关键字调用的函数的行为称为实例化
构造函数内部不需要写return,构造函数自动返回创建的新对象
通过构造函数创建的对象称为实例对象,实例对象中的属性和方法称为实例成员(实例属性和实例方法)
说明:
静态成员
构造函数的属性和方法被称为静态成员(静态属性和静态方法)
说明:
比如Date.now( ) Math.PI Math.random( )
总结:
1.实例成员(属性和方法)写在谁身上?
实例对象的属性和方法即为实例成员
实例对象相互独立,实例成员当前实例对象使用
2.静态成员(属性和方法)写在谁身上?
构造函数的属性和方法被称为静态成员
静态成员只能通过构造函数来访问
JS中最主要的数据类型有6种
基本数据类型:字符串、数值、布尔、undefined、null
引用类型:对象
const str ="pink"
//js 底层完成,把简单数据类型包装成了引用数据类型
//const str =new String ('pink')
console.log(str.length);
const num=12
console.log(num.toFixed(2));//num.toFixed()是保留几位小数
其实字符串、数值、布尔等基本类型也都有专门的构造函数,这些我们称为包装类型
JS中几乎所有的数据都可以基于构造函数创建
内置构造函数分为引用类型和包装类型
引用类型
Object、Array、RrgExp、Date等
包装类型
String、Number、Boolean等
// const str ="pink"
//js 底层完成,把简单数据类型包装成了引用数据类型
//const str =new String ('pink')
console.log(str.length);
const num=12
console.log(num.toFixed(2));//num.toFixed()是保留几位小数
是内置的构造函数,用于创建普通对象
const o={uname:'pink', age:18}
//1.获得所有的属性名
console.log(Object.keys(o));//返回数组['uname','age']
//获取所有的属性值
console.log(Object.values(o));//['pink',18]
//拷贝对象
const oo={}
Object.assign(oo,o)
console.log(oo);
//经常使用的场景:给对象添加属性
Object.assign(o,{gender:"女"})
console.log(o);
是内置的构造函数,用于创建数组
创建数组建议使用字面量创建,不用array构造函数创建
方法 | 作用 | 说明 |
---|---|---|
forEach | 遍历数组 | 不返回数组,经常用于查找遍历数组元素 |
filter | 过滤数组 | 返回新数组,返回的是筛选满足条件的数据组元素 |
map | 迭代数组 | 返回新数组,返回的是处理之后的数组元素,想要使用返回的新数组 |
reduce | 累计器 | 返回累计处理的结果,经常用于求和等 |
//数组reduce方法
//arr.reduce(function(上一次值,当前值){},初始值)
const arr =[1,5,6,8]
//1.没有初始值
// const total=arr.reduce(function(prev,current){
// return prev+current
// })
// console.log(total);
//2.有初始值
const result=arr.reduce(function(prev,current){
return prev+current
},10)
console.log(result);
//3.箭头函数的写法
const total=arr.reduce((prev,current)=>prev+current,10)
console.log(total);
没有起始值,循环次数比数组元素个数少一次
有起始值,循环次数等于数组元素个数
// const arr=['red','blue','green']
// const result=arr.find(function(item){
// return item==='blue'
// })//会将blue作为返回值返回
// console.log(result);
//find最常使用的场景
const arr=[
{
name:'小米',
price:1999
},
{
name:'华为',
price:3999
}
]
//找小米这个对象,并返回这个对象
// arr.find(function(item){
// // console.log(item);
// // console.log(item.name);
// return item.name==='小米'
// })
const mi=arr.find(item=>item.name==="小米")
console.log(mi);
every()方法测试一个数组内的所有元素是否都能通过某个指定函数的测试。返回一个布尔值
//every()每一个是否都符合条件,如果都符合返回true,否则返回false
const arr=[10,20,30]
const flag=arr.every(item=>item>=20)
console.log(flag);
some()方法测试一个数组内是否含有元素能通过某个指定函数的测试。返回一个布尔值
//some()至少有一个是否符合条件,如果有返回true,否则返回false
const arr=[10,20,30]
const flag=arr.some(item=>item>=20)
console.log(flag);
const spec={size:'40cm*40cm',color:'黑色'}
//1.所有的属性值获取过来
console.log(Object.values(spec));
//2.转换为字符串 join('/')把数组根据分隔符转化为字符串
console.log( Object.values(spec).join('/'));
document.querySelector('div').innerHTML= Object.values(spec).join('/')
静态方法 Array.from( )
<body>
<ul>
<li>1li>
<li>2li>
<li>3li>
ul>
<script>
const lis =document.querySelectorAll('ul li')
console.log(lis);//获取过来的是伪数组NodeList(还有arguments获取的也是伪数组)
// lis.pop();//伪数组无法使用pop删除方法
const liss=Array.from(lis)
console.log(liss);
liss.pop()//真数组可以使用
console.log(liss);
script>
body>
//把字符串转化为数组,与join超级相似,根据分隔符将其分割成数组
const str ='pink ,red'
const arr=str.split(',')
console.log(arr);
const str1 ='2022-4-8'
const arr1=str1.split('-')
console.log(arr1);
//字符串截取 substring(开始的索引号[,结束的索引号])
const str='再也不用做核酸了'
console.log(str.substring(5))//如果省略了结束索引号,默认取到最后
console.log(str.substring(5,6))//截取的部分不包括结束索引
//startswith()用来判断当前字符串是否以另外一个给定的字符串开头,并根据判断结果返回true或false
var str="To be, or not to be, that is the question."
alert(str.startsWith("To be")); //true
alert(str.startsWith("not to be")); //false
alert(str.startsWith("not to be",10)); //true 加空格
//includes 判断某个字符是否包含在一个字符串内
var str="To be, or not to be, that is the question."
console.log(str.includes('To be')); //true
console.log(str.includes('question')); //true
console.log(str.includes('nonexistent')); //false
console.log(str.includes('To be',1)); //false
console.log(str.includes('To BE')); //false
console.log(str.includes('To b')); //true
JS面向函数编程
封装是面向对象思想中比较重要的一部分,JS面向对象可以通过构造函数实现封装。
同样的将变量和函数组合到了一起并能通过this实现数据的共享,所不同的是借助构造函数创建出来的实例对象之间是彼此不影响的。
总结:
之前我们学习的构造函数很好用,但存在浪费内存的问题
原型是一个对象,我们也称prototype为原型对象
原型的作用:
构造函数和原型里面的this指向实例化的对象
每个原型对象里面都有一个constructor属性
使用场景:
如果有多个对象的方法,我们可以给原型对象采取对象形式赋值
但是这样就会覆盖构造函数原型对象原来的内容,这样修改后的原型对象constructor就不在指向当前构造函数了。此时,我们可以在修改后的原型对象中,添加一个constructor指向原来的构造函数
//constructor 构造函数
//该属性指向该原型对象的构造函数,简单理解,就是指向我的爸爸,我是有爸爸的孩子
function Star(){
}
// const ldh=new Star()
// console.log(Star.prototype);
// console.log(Star.prototype.constructor===Star);
// Star.prototype.sing=function(){
// console.log('我会唱歌');
// }
// Star.prototype.dance=function(){
// console.log('我会跳舞');
// } //这样写显得繁琐
console.log(Star.prototype);
Star.prototype={
//从新指回创造这个原型对象的构造函数
constructor:Star,
sing:function(){
console.log('我会唱歌');
},
dance:function(){
console.log('我会跳舞');
},
}
console.log(Star.prototype);
作用:指向该原型对象的构造函数
对象都会有一个属性__proto__指向构造函数的prototype原型对象,之所以我们对象可以使用构造函数prototype原型对象的属性和方法,就是因为对象有__proto__原型的存在。
function Star(){ }
const ldh=new Star()
//对象原型__proto__ 指向该构造函数的原型对象
console.log(ldh.__proto__);
console.log(ldh.__proto__===Star.prototype); //true
//对象原型里面有constructor指向构造函数Star
console.log(ldh.__proto__.constructor);
注意:
总结:
1.prototype是什么?从哪里来?
2.constructor属性在哪里?作用干啥的?
3.__proto__属性在哪里?指向谁?
继承是面向对象编程的另一个特征,通过继承进一步提升代码封装的程度,JavaScript中大多是借助原型对象实现继承的特性
原型继承中出现的问题:
都同时使用了同一个对象,根据引用类型的特点,他们指向同一个对象,修改一个就会都影响
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YZOMRaKJ-1681011543044)(C:\Users\86184\Desktop\4.3-4.14\原型继承出现的问题.png)]
解决问题的思路:让Woman和Man指向的对象不同,内容依然相同
解决问题的方法:构造函数 new出来的对象 结构一样 但对象不一样
// //男人,女人都继承了人这个特性
// //继续抽取
// const Person = {
// eyes: 2,
// head: 1
// }
//构造函数 new出来的对象 结构一样 但对象不一样
function Person(){
this.eyes=2
this.head=1
}
//女人 构造函数 想要继承Person
function Woman() {
// this.eyes = 2
// this.head = 1
}
//Women 通过原型来继承Person
// Woman.prototype = Person
Woman.prototype =new Person()
//指回原来的函数
Woman.prototype.constructor = Woman
//给女人添加一个方法 生孩子
Woman.prototype.baby=function(){
console.log('宝贝');
}//这样添加之后,男女都有baby,麻烦大了
//出现该问题的原因
//男人和女人都同时使用了同一个对象,根据引用类型的特点,他们指向同一个对象,修改一个就会都影响
//解决问题的思路:让Woman和Man指向的对象不同,内容依然相同
//解决问题的方法:构造函数 new出来的对象 结构一样 但对象不一样
const red = new Woman()
console.log(red);
console.log(Woman.prototype);//指回了原来的函数,既继承了属性,又有了constructor
// console.log(red.eyes);
//男人 构造函数 想要继承Person
function Man() {
// this.eyes = 2
// this.head = 1
}
//Man通过原型继承Person
Man.prototype=new Person()
//指回原来的函数
Man.prototype.constructor=Man
const pink = new Man()
console.log(pink);
console.log(Man.prototype);
基于原型对象的继承是的不同构造函数的原型对象关联在一起,并且这种关联的关系是一种链状的结构,我们就爱那个原型对象的链状结构称为原型链
在这里粘贴一个博客,觉得讲的挺好的
(37条消息) JavaScript完整原型链图解_js原型链图示_旦旦boom的博客-CSDN博客
//自己定义 数组扩展方法 求和 和 最大值
//1. 我们定义的这个方法,任何一个数组实例对象都可以使用
//2. 自定义的方法写(挂载)到 数组.prototype身上
//求最大值
// const arr=[1,2,3]
// Array.prototype.max=function(){
// //展开运算符
// return Math.max(...this)
// }
// console.log(arr.max());
//求最小值
// const arr=[1,2,3]
// Array.prototype.min=function(){
// return Math.min(...this)
// }
// console.log(arr.min());
//求和 方法
const arr=[1,2,3]
Array.prototype.sum=function(){
return this.reduce((prev,item)=>prev+item,0)
}
console.log(arr.sum());