本文已参与「新人创作礼」活动, 一起开启掘金创作之路。
原型链
面向对象特性
面向对象三大特征: 封装、继承、多态
1.封装 : 把代码放入对象的方法中
2.继承 : 一个对象(子对象) 拥有 另一个对象(父对象) 所有的成员
- js核心就是继承
3.多态 : 一个对象在不同情况下的多种状态
- js基本不涉及
原型继承
Js的核心是继承,那么我们是怎样实现继承的
我们js其实是是通过原型实现的,具体实现方法是 父对象作为子对象构造函数的原型
let Person = {
name : 'hhh',
eat(){
console.log('想吃肉');
}
}
function Son(name, age) {
this.name = name;
this.age = age;
}
Son.prototype = Person;
//将儿子的原型指向父 这样父里面的内容儿子都可以访问到
let s1 = new Son('芒果',18);
console.log(s1.name);
console.log(s1);
s1.eat();
原型链
原型链是什么?
每一个对象都有自己的原型,而原型也是对象,也有自己的原型,以此类推形成的链式结构我们称为原型链,原型链的顶端是null
对象访问原型链的原则
原型链的本质其实是js的查找机制(就近原则),如果自己没有就找原型,自己的原型没有就找原型的原型,以此类推,如果到顶端还没有的话,如果是属性就返回undefined,如果是方法就睡报错
原型链的作用
继承
instacneof运算符
instanceof(关键字): 检测构造函数原型在不在实例对象的原型链中
let arr = [10, 20, 30];
// arr.__proto__ -> Array.prototype -> Objct.prototype -> null
console.log(arr instanceof Array); //true
console.log(arr instanceof Object); //true
console.log(arr instanceof String); //false
instanceof 和 typeof的区别
instanceof是检测构造函数原型在不在实例对象的原型链中
typeof是检测数据类型的
函数补充
arguments
arguments关键字: 获取函数所有的实参
arguments特点
- arguments 能处理多个参数,它是一个伪数组
- arguments 只能在函数内部使用,使用arguments后函数能够接受多个参数
- 函数使用arguments后移入这个函数,提示信息中参数位置有...表示这个函数使用了arguments
rest
arguments能够拿到所有的实参
而rest能拿到剩余的实参(没有被使用的实参)
语法 ...形参
rest得到的是一个真数组
应用:绝大部分情况下,rest可以代替arguments
function fn(a,b,...rest){
console.log(a,b);
console.log(arguments);
console.log(rest);
}
fn(10,20,30,40,50,60,70,80);
/*
10 20
Arguments(8) [10, 20, 30, 40, 50, 60, 70, 80]
rest(6) [30, 40, 50, 60, 70, 80]
*/
默认参数
默认参数是ES6的新语法
我们在以前想要让参数具有默认值都是用的逻辑中断,但es6之后我们可以直接在形参里面赋值,相当于默认值,如果传入值就用传入的值,没有传入就用默认
//用逻辑中断的方法
function f(a, b) {
a = a || 1;
b = b || 2
console.log(a + b);
}
f(); //3
//使用es6默认参数的新语法
function f(a=1, b=2) {
console.log(a + b);
}
f();
class类
class的使用
ES6原型新语法:class (class就是构造函数另一种写法,功能和prototype完全一致,只是写法不同)
class类函数 : 比ES5构造函数语法更加 简洁、易读
特点:
(1)class类函数 必须要使用new调用, 否则程序报错
(2)把构造函数和原型成员 写在一个大括号里面,提高阅读性
语法:
class 类函数{
//构造函数
constructor(){}
//原型方法
eat(){}
}
class Person{
//构造函数
constructor(name,age){
this.name = name
this.age = age
}
//原型方法,使用cfalss可以直接将方法写在class声明的{}里面
eat(){
console.log('我喜欢干饭')
}
learn(){
console.log('学习')
}
}
//使用class类函数之后,之前学习过的prototype所有语法全部通用
Person.prototype.type = '哺乳动物'
//实例对象
let p1 = new Person('mg',22)
console.log(p1)
extends继承
extends : 原型链继承
语法 :
class 子类函数 extends 父类函数{
//子类自己的原型方法
}
底层原理 : 替换原型继承
子类型函数.prototype.proto = 父类函数.prototype
class Person{
//构造函数
constructor(name,age){
this.name = name
this.age = age
}
//原型方法
eat(){
console.log('我喜欢干饭')
}
learn(){
console.log('学习')
}
}
/* 学生构造函数 */
class Student extends Person{
//学生的原型的方法
play(){
console.log('劳逸结合')
}
}
let s1 = new Student('mg',22)
s1.eat(); //我喜欢干饭
super
class Person{
//构造函数
constructor(name,age){
this.name = name
this.age = age
}
//原型方法
eat(){
console.log('我喜欢干饭')
}
learn(){
console.log('学习')
}
}
/* 学生构造函数 */
class Student extends Person{
//子类自己的构造函数
constructor(name,age,score){
/* 继承之后,子类不允许自己写constructor.
如果子类想要自己写constructor, 必须要先调用super()
*/
super(name.age);// 相当于调用了父亲的构造函数
this.score = score;
}
//学生的原型的方法
play(){
console.log('劳逸结合')
}
}
let s1 = new Student('mg',22)
s1.eat(); //我喜欢干饭