构造函数
类是对象的模板,对象是类的实例
而在es6之前,js并没有引入类的概念
es6 全称为 ECMAScript6.0 , 在es6之前,对象不是基于类创建的,而是一种称为构建函数的特殊函数来定义和它们的特征
也就是说,在es6之前,创建对象是利用构造函数创建的,在es6之后,利用class 类 来创建对象
创建
1、利用构造函数创建
构造函数是一种特殊的函数,主要用来初始化对象; 就是为对象成员变量赋初始值
构造函数与new一起使用,我们把对象中的一些公共的属性和方法抽取出来,然后封装到这个函数里面
使用构造函数注意事项
在JS中,使用构造函数时要注意:
1、首字母大写
2、构造函数要与new 一起使用才有意义
new 在执行时会做的四件事:
1、在内存中创建一个空对象
2、让this指向这个空对象
3、执行构造函数里面的代码,给这个新对象添加属性和方法
4、返回这个新对象(所以构造函数里面不需要return)
实例成员和静态成员
实例成员
实例成员 : 在构造函数内部通过this添加的,访问的时候只能通过实例化对象来访问
静态成员
静态成员 : 在构造函数本身上面添加的,访问的时候,只能通过构造函数本身来访问
原型 prototype
为什么需要原型?
用构造函数来创建对象的方法很好用,但是存在浪费内存的问题
我们希望所有的对象使用同一个函数, 这样就比较节省内存,那么我们可以使用原型对象
如何去使用
构造函数通过原型对象分配的函数是所有对象所共享的 。
js规定,每一个构造函数都有一个prototype属性,指向另一个对象;这个prototype就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有
我们可以把那些不变的方法,直接定义在prototype对象上,这样所有对象的实例就都可以共享这些方法了。
那么此时dk的sing和joshua的sing就相等了
总结原型
-
原型 就是一个对象,所以我们也称prototype为原型对象
-
原型的作用是 : 共享方法
-
一般情况下,我们公共属性定义到构造函数中,公共的方法放到原型对象上
对象原型 _ proto _ (每边2个_ )
定义
对象都会有一个属性 _ proto _ ,指向构造函数的prototype原型对象,之所以我们对象可以使用构造函数prototype原型对象的属性和方法,就是因为对象有 _ _ proto _ _ 原型的存在
(对象原型 _ _ proto_ _ 指向的是原型对象)
- 对象中的_ _ proto_ _ 和 构造函数的prototype的一样的
方法查找规则
- 方法查找规则 : 首先看对象是否有这个方法,若有就执行这个对象上的方法;若没有,就去构造函数原型对象prototype 上去寻找
_ proto _ 对象原型的意义
- _ proto _ 对象原型的意义就在于为对象的查找机制提供一个方法,或者说一条路线,但它是一个非标准的,因此在实际开发中,不可以使用这个属性,它只是内部指向原型对象prototype
constructor 构造函数
定义
对象原型__proto__ 和 构造函数原型对象prototype里面都有一个属性 constructor属性,constructor我们称为构造函数,因为它指回构造函数本身
使用
constructor主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数
但输出 constructor 时,指向的却不是 原来的构造函数
这样是因为原来的构造函数的prototype是添加方法,而现在这个是 把新添加的多个方法赋值给prototype ,所以里面原有的 constructor不存在了。
那么我们需要在prototype中重新声明一下
- 那么若我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数
那么此时的constructor里面就指向了原来的构造函数
构造函数、实例和原型对象的三者之间的关系
原型链
图解
js的成员查找机制(规则)
-
当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性
-
若没有就去找它的原型 (也就是对象._ proto_ 指向的是 prototype原型对象)
-
若还没有,就去查找原型对象的__proto__ (也就是Object的原型对象)
-
以此类推一直找到Object.prototype._ proto_ 为止(null)
原型对象的this指向
1、在构造函数中的this ,指向的是 实例对象
2、在构造函数的原型对象函数里面的this,指向的是 实例对象
注 : 必须调用了,才能知道此时的this 指向谁
利用原型对象扩展内置对象
可以通过原型对象,将原来的内置对象进行扩展自定义的方法。比如给数组增加自定义求偶数和的功能
注 : script执行代码是从上到下的,所以先添加方法,再创建实例对象
还可以利用 new Array来创建数组
错误写法:
数组和字符串内置对象不可以给原型对象覆盖操作Array.prototype = {} ,只能是 Array.prototype.xxx = function(){} 的方法
继承
es6之前没有提供 extends继承,我们可以通过 构造函数 + 原型对象 模拟实现继承,被称为组合继承
call()
作用 : 调用函数 和 改变this指向
调用函数
构造函数名.call()
改变this指向
call() 可以改变 函数里面的this指向
() 里面无参数
() 里面有参数
利用call借用构造函数继承父类型属性
因为es6之前没有class类,所以无法使用extends来继承
1、定义父构造函数
2、定义子构造函数
3、此时 想要继承父构造函数的属性 , 那么就利用call方法
4、实例化子构造函数
继承 : 子继承父,子需要继承父里面的方法属性,
那就在子构造函数中 把父构造函数的 this 指向指向子构造函数
所以,此时的 Father 里面的 this 指向的是子构造函数
然后把 uname 和 age 参数传进去
利用call在原型对象继承方法
1、定义父构造函数
2、在父构造函数中把共用方法写在原型上
3、定义子构造函数
4、在子构造函数中,要继承父构造函数的方法,所以要利用call() 调用父构造函数
5、(错误写法使用 Son.prototype = Father.prototype ,就是把父构造函数中的原型对象给子构造函数的原型对象(子构造函数原型对象 指向 父构造函数的原型对象),但此时子构造函数单独拥有的原型对象也给父构造函数了
图解 :
6、正确使用方法是
7、此时的constructor函数
8、指回Son的构造函数
图解 :
类的本质
es6之前 ,构造函数
es6之前,通过 构造函数 + 原型 实现面向对象编程
-
构造函数的特点 :
1、构造函数有原型对象 prototype
2、构造函数原型对象prototype里面有constructor,指向构造函数本身
3、构造函数可以通过原型对象添加方法
4、构造函数创建的实例对象有__proto__ 原型指向 构造函数的原型对象
es6之后,类class
定义
类的本质是一个函数,可以简单的认为类就是构造函数的另外一种写法
特点
-
特点 :
1、类里面有原型对象 prototype
2、类里面的prototype也有constructor,指向的是类本身
3、类也可以通过原型对象添加方法
4、类创建的实例对象__proto__ 原型 会指向 类的原型对象
ES5中的新增方法
遍历(迭代)数组
forEach
forEach 会遍历数组里面的元素
array.forEach(function(currentValue,index,arr){})
1、currentValue : 数组当前项的值
2、index : 数组当前项的索引
3、arr : 数组对象本身
map
会返回一个新的数组,map是对数组里面的每一个元素都进行相同的操作
array.map(function(currentValue,index,arr){})
filter
会返回一个新的数组,对旧数组中里面的元素进行检查是否符合条件;主要用于筛选数组
array.filter(function(currentValue,index,arr){})
some
是检查数组中的元素是否满足指定条件 ; 也就是说,查找数组中是否有满足指定条件的元素
array.some(function(currentValue,index,arr){})
1、它返回的是布尔值,若查找到这个元素,就返回true;若找不到就返回false
2、若找到第一个满足条件的元素,那么就终止循环,不需要继续查找
3、currentValue : 数组当前项的值
4、index : 数组当前项的索引
5、arr : 数组对象本身
filter 和 some 的区别
filter 和some 都是进行筛选的 , 不过filter返回的是一个新的数组,里面包含的也是所有满足条件的元素
some 返回的是布尔值,找到就true ; 没有找到就是false ; 而且 some 是找到第一个就不会再继续查找下一个了
every
检测数组中的所有元素是否都满足条件,若有一项不满足,那么返回false ; 都满足则就返回true
array.every(function(currentValue,index,arr){})
2、返回的是布尔值
3、返回一个新的数组
4、不会对空数组进行检测
因为不会对空数组进行检测,所以 当every遇到空数组的时候,就会直接返回true
every 和 some 的区别
every 和 some 返回的都是布尔值 ; 不过 every是判断整个数组里面的值是否满足条件,若都满足则返回true , 否则返回false 。
而 some 是 判断数组中是否有满足条件的值,若有则返回true ,没有就false
some 和 forEach 区别
some 遇到 return true 则终止迭代,迭代效率更高 ; 而 forEach 和 filter 不一样 ,里面写return true 不会终止迭代
字符串
trim 去除字符串两侧空白字符
trim()
1、去除字符串两侧的空白字符 , 不会去除中间的
2、会生成一个新的字符串,所以要接收这个新字符串
Object.defineProperty(obj,prop,descriptor)
作用 : 定义对象中新属性 或 修改原有的属性
1、obj : 必需 。 目标对象
2、prop : 必需 。 需定义或修改的属性的名字
3、descriptor(说明) : 必需 。 目标属性所用于的特性 ; 以对象的形式 {} 书写 ,
若对象中没有该属性则进行添加 ; 若有该属性则进行修改
descriptor 里面的 value
设置属性的值 。 默认 undefined
descriptor 里面的 writable
值是否可以重写 。 true | false 默认false
descriptor 里面的 enumerable
目标属性是否可以被枚举 。 true | false 默认false
注 : 前面创建的num无法遍历出来,是因为 enumerable 默认就是false 无法遍历
descriptor 里面的 configurable
不允许修改第三个属性里面的特性
若修改会报错
遍历对象 Object.keys(对象名)
返回的是由属性名组成的数组
1、和 for in 差不多,都是遍历对象的
2、不过,它返回的是一个 由属性名组成的数组
3、好处是 : 因为返回的是一个数组,所以可以直接使用forEach 进行遍历