JavaScript面向对象
- 面向过程强调的是功能行为,关注的是解决问题需要哪些步骤
- 面向对象是将功能封装进对象,强调具备了功能的对象,关注的是解决问题需要哪些对象
面向对象的特点
- 是一种符合人们思考习惯的思想
- 可以将复杂的事情简单化
- 将程序员从执行者转换成了指挥者
创建默认对象
JavaScript中提供了一个默认的类Object,我们可以通过这个类来创建对象
-
由于我们是使用系统默认的类创建的对象,所以系统不知道我们想要什么属性和行为,我们必须手动的添加我们想要的属性和行为
- 如何给一个对象添加属性: 对象名称.属性名称 = 值;
- 如果给一个对象添加行为: 对象名称.行为名称 = 函数;
-
创建对象第一种方式
let 对象名称 = new Object(); 对象名称.属性名称 = 值; 对象名称.行为名称 = 方法;
-
创建对象第二种方式
let 对象名称 = {}; 对象名称.属性名称 = 值; 对象名称.行为名称 = 方法;
-
创建对象第三种方式
let 对象名称 = { 属性名称:值, 行为名称:方法; }
方法和函数区别
- 函数就是没有和其他的类显示的绑定在一起的,就称之为函数,可以直接调用
- 方法就是显示的和其他的类显示的绑定在一起的,就称之为方法,只能通过对象来调用
- 无论是函数还是方法,内部都有一个叫this的东西,谁调用了当前的函数或者方法,那么当前的this就是谁,函数内部的this输出的是window,方法内部的this输出的是当前调用的那个对象
工厂函数不专业
-
工厂函数就是专门用于创建对象的函数
function 函数名称(形参列表){ let 对象名称 = new Object(); 对象名称.属性名称 = 形参; 对象名称.行为名称 = 方法; return 对象名称; }
构造函数
构造函数和工厂函数一样,都是专门用于创建对象的,本质上是工厂函数的简写
-
构造函数和工厂函数的区别
- 构造函数的函数名称首字母必须大写
- 构造函数只能通过new来调用
function 构造函数名称(形参列表){ let 对象名称 = new Object(); //系统自动添加 let this = 对象名称; //系统自动添加 this.属性名称 = 形参; return this; //系统自动添加 } 构造函数名称.prototype = { 行为名称:方法 } 当我们通过new来调用函数时 1.会在构造函数中自动创建一个对象 2.会自动将刚才创建的对象赋值给this 3.会在构造函数最后自动添加return this;
-
prototype的特点
- 储存在prototype中的方法可以被对应构造函数创建出来的所有对象共享
- prototype中除了可以存储方法以外,还可以存储属性
- prototype中如果出现了和构造函数中同名的属性或者方法,对象在访问的时候,访问到的是构造函数中的数据
-
prototype应用场景
- prototype中一般情况下用于存储所有对象都相同的一些属性以及方法
- 如果是对象特有的属性或者方法,我们会存储到构造函数中
对象三角恋关系
- 每个
构造函数
中都有一个默认的属性,叫做prototype,prototype属性保存着一个对象,这个对象我们称之为原型对象
- 每个
原型对象
都有一个默认的属性,叫做constructor,constructor指向当前原型对象对应的那个构造函数
- 通过构造函数创建出来的对象称之为
实例对象
,每个实例对象中都有一个默认的属性,叫做__proto__,__proto__指向创建它的那个构造函数的原型对象
Function函数
- JavaScript中函数是引用类型(对象类型),既然是对象,所以也是通过构造函数创建出来的,
所有函数
都是通过Function构造函数创建出来的对象 - JavaScript中只要是
函数
就由prototype属性,Function函数
的prototype属性指向Function原型对象
- JavaScript中只要是
原型对象
就有constructor属性,Function原型对象
的constructor指向它对应的构造函数 -
Function构造函数
也是一个对象, 所以也有__proto__属性,Function构造函数__proto__属性指向Function原型对象
Object函数
- JavaScript中还有一个系统提供的构造函数叫做Object,只要是函数都是"Function构造函数"的实例对象
- 只要是对象就有__proto__属性,所以Object构造函数也有__proto__属性,Object构造函数的__proto__属性指向创建它那个构造函数的原型对象
- Object原型对象的__proto__属性指向NULL
函数对象完整关系
- 所有的构造函数都有一个prototype属性, 所有prototype属性都指向自己的原型对象
- 所有的原型对象都有一个constructor属性, 所有constructor属性都指向自己的构造函数
- 所有函数都是Function构造函数的实例对象
- 所有函数都是对象, 包括Function构造函数
- 所有对象都有__proto__属性
- 普通对象的__proto__属性指向创建它的那个构造函数对应的"原型对象"
- 所有对象的__proto__属性最终都会指向"Object原型对象"
- "Object原型对象"的__proto__属性指向NULL
- "Function构造函数"的__proto__属性指向自己的"原型对象"
原型链
- 对象中__proto__组成的链条我们称之为原型链
- 对象在查找属性和方法的时候,会先在当前对象查找
- 当前对象找不到想要的,会一次去上一级原型对象中查找
- 找到Object原型对象都没有找到,就会报错
- 为了不破坏原型链的关系,在给原型对象赋值时,需要手动添加constructor指向对象的构造函数
- 属性注意点:在给一个对象不存在的属性设置值的时候,不会去原型对象中查找,如果当前对象没有就会给当前对象新增一个不存在的属性
Js三大特性
封装性
- 对象的私有变量和函数
- 默认情况下对象中的属性和方法都是公有的,只要拿到对象就能操作对象的属性和方法
- 外界不能直接访问的变量和函数就是私有变量和私有函数
- 构造函数的本质也是一个函数,所以也会开启一个新的作用域,所以在构造函数中定义的变量和函数就是私有变量和函数
- 封装性就是隐藏实现细节,仅对外公开结构
- 封装就是将数据隐藏起来,只能用此类的方法才可以读取或者设置数据,不可被外部任意修改,封装是面向对象设计本质
将变化隔离
,这样降低了数据被误用的可能提高安全性和灵活性
属性方法分类
- 实例属性/实例方法
- 在企业开发中通过实例对象访问的属性,就称之为实例属性
- 在企业开发中通过实例对象调用的方法,就称之为实例方法
- 静态属性/静态方法
- 在企业开发中通过构造函数访问的属性,就称之为静态属性
- 在企业开发中通过构造函数调用的方法,就称之为静态方法
继承性
- 在企业开发中如果构造函数和构造函数之间的关系是is a关系,那么就可以使用继承来优化代码,来减少代码的冗余度
- bind-call-apply
- bind方法作用: 修改函数或者方法中的this为指定的对象, 并且会返回一个修改之后的新函数给我们,bind方法除了可以修改this以外, 还可以传递参数, 只不过参数必须写在this对象的后面
- call方法作用:修改函数或者方法中的this为指定的对象, 并且会立即调用修改之后的函数,call方法除了可以修改this以外, 还可以传递参数, 只不过参数必须写在this对象的后面
- apply方法作用:修改函数或者方法中的this为指定的对象,并且会立即调用修改之后的函数,apply方法除了可以修改this以外,还可以传递参数,只不过参数必须通过
数组的方式
传递
- js中继承的中级方法
- 在子类的构造函数中通过call借助父类的构造函数
- 将子类的原型对象修改为父类的实例对象
多态性
- 强类型语言与弱类型语言
- 一般编译型语言都是强类型语言,要求变量的使用要严格符合规定
- 一般解释型语言都是弱类型语言,不会要求变量的使用要严格符合定义
- 由于js语言是弱类型语言,所有我们不用过多关注多态
- 多态是指事物的多种状态
- 父类型变量保存子类型对象, 父类型变量当前保存的对象不同, 产生的结果也不同,这是多态在编程语言中的体现
ES6类和对象
在ES6之前通过构造函数来定义一个类
-
从ES6开始系统提供了一个名称叫做class的关键字,这个关键字就是专门用于定义类的
class 名称{ //当我们通过new创建对象的时候,系统会自动调用constructor constructor(myName,myAge){ this.name = myName; this.age = myAge; } //下面一行代码默认会添加到原型对象中 方法名称(){} //静态方法 static 方法名称(){} }
-
ES6中如何继承
- 在子类后面添加extends并指定父类的名称
- 在子类的constructor构造函数中通过super方法借助父类的构造函数
-
注意点
- 在ES6标准中添加实例属性都需要在
- constructor中添加
- 在ES6中static只支持定义静态方法不支持定义静态属性
获取对象类型:根据对象名称.constructor.name
输出来判断
instanceof关键字:用于判断对象
是否是指定构造函数的实例
,只要构造函数的原型对象出现在实例对象的原型链中都会返回true
isPrototypeOf属性:用于判断一个对象
是否是另一个对象的原型
,只要调用者在传入对象的原型链上都会返回true
判断对象属性(会查找原型中):通过"属性名称" in 对象
返回的值来判断
判断自身是否拥有某个属性:通过对象.hasOwnProperty("属性名称")
返回的值来判断
对象遍历
- 对象遍历就是依次取出对象中所有的属性和方法
- 在js中可以通过高级for循环来遍历对象
-
for(let key in 对象)
将指定对象中所有的属性和方法的名称取出来依次赋值给key这个变量
对象解构赋值
- 对象的解构赋值和数组的解构赋值除了符号不一样,其他的一模一样
- 数组解构使用[],对象解构使用{}
- 在对象解构赋值中,左边的变量名称必须和对象的属性名称一致,才能解构出数据
深拷贝和浅拷贝
- 深拷贝:修改新变量的值不会影响原有变量的值,默认情况下基本数据类型都是深拷贝
- 浅拷贝:修改新变量的值会影响原有的变量的值,默认情况下引用类型都是浅拷贝
数组高级API
-
forEach(遍历数组)
//创建一个自己的forEach Array.prototype.myForEach = function(fn){ for(let i = 0;i < this.length;i++){ fn(this[i],i,this) } } //使用自己创建的forEach arr.myForEach(function(value,index,array){ console.log(value) })
-
findIndex/find(数组查找)
//创建一个自己的findIndex Array.prototype.myFindIndex = function(fn){ for(let i = 0;i < this.length;i++){ //找到了就返回索引 if(fn(this[i],i,this)){ return i; } } //没找到就返回-1 return -1; } //使用自己创建的findIndex arr.myFindIndex(function(value,index,array) { if(value === xxx){ return true; } })
//创建一个自己的find Array.prototype.myFind = function(fn){ for(let i = 0;i < this.length;i++){ //找到了就返回元素 if(fn(this[i],i,this)){ return this[i]; } } //没找到就返回undefined return undefined; }
-
filter/map
//创建自己的filter Array.prototype.myFilter = function(fn){ //创建一个新数组+ let newArr = []; for(let i = 0;i < this.length;i++){ //把符合条件的添加到一个新数组中 if(fn(this[i],i,this)){ newArr.push(this[i]) } } //返回新数组 return newArr; } //使用自己创建的filter arr.myFilter(function(value,index,array){ if(value === xxx){ return true; } })
//创建自己的map Array.prototype.myMap = function(fn){ //创建一个同样长度的数组 let newArr = new Array(this.length); //把数组中的元素全部设置为undefined newArr.fill(undefined); for(let i = 0;i < this.length;i++){ //把符合条件的替换掉原来的undefined if(fn(this[i],i,this)){ newArr[i] = this[i]; } } //返回新数组 return newArr; }
- 数组排序:sort
- 获取字符串长度: .length
- 获取某个字符串: [索引]/charAt
- 字符串查找: indexOf/lastIndexOf/includes
- 拼接字符串: concat(不推荐)/ +
- 截取子串: slice/substring/substr
- 字符串切割: split
- 是否以指定字符串开头: startsWith
- 是否以指定字符串结尾: endsWith
- 字符串模板: ``
基本数据类型和基本包装类型
- 基本数据类型
- 字符串类型/数值类型/布尔类型/空类型/未定义类型
- 通过字面量创建的基本数据类型的数据都是常量
- 常量是不能被修改的,每次修改或者拼接都是生成一个新的
- 对象类型的特点
- 有属性和方法
- 以前之所以能够访问基本数据类型的属性和方法,是因为在运行时系统自动将基本数据类型包装成了对象类型
三大对象
- JavaScript中提供三种自带的对象,分别是"本地对象"/"内置对象"/"宿主对象",宿主是指JavaScript
运行环境
,js可以再浏览器中运行,也可以在服务器上运行(nodejs) - 本地对象
- 与宿主无关,无论在浏览器还是服务器中都有的对象
- 就是ECMAScript标准中定义的类(构造函数)
- 在使用过程中
需要手动new创建
- 例如:Boolean,Number,String,Array,Function,Object,Data,RegExp等
- 内置对象
- 与宿主无关,无论在浏览器还是服务器中都有的对象
- ECMAScript已经帮我们创建好的对象
- 在使用过程中
无需手动new创建
- 例如:Global,Math,JSON
- 宿主对象
- 对于嵌入到网页的js来说,其宿主对象就是浏览器,所以宿主对象就是浏览器提供的对象
- 包含:Window和Document等
- 所有的DOM和BOM对象都属于宿主对象
- 自定义对象:我们自己编写的类创建的对象
- Math内置对象
- Math.floor() 向下取整
- Math.ceil() 向上取整
- Math.round() 四舍五入
- Math.abs() 绝对值
- Math.random() 生成随机数