前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片NEW
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片NEW
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
  • JavaScript Part 1

    • Ajax总结篇
    • Canvas 绘制八大行星
    • DOM编程之API学习总结篇
    • JavaScript事件机制
    • JavaScript代码片段100个
    • JavaScript作用域分析总结
    • JavaScript原型链回顾
    • JavaScript及jQuery中的各种宽高属性图解
    • JavaScript工程项目的一系列最佳实践
    • JavaScript常用API合集
    • JavaScript数组、字符串、对象常用方法
    • JavaScript数组方法总结篇
    • JavaScript深浅拷贝
    • JavaScript运动框架之速度时间版本
    • JavaScript运行机制Event Loop
    • JavaScript防抖节流原理
    • Javascript中的复制粘贴功能
    • Javascript数组详解
    • OOP之原型与原型链
    • Object.defineProperty详解
    • V8源码浅析JS数组常见方法
    • await 在 forEach 中不生效解决方案
    • iframe+表单跨域提交POST请求
    • javascript笔记总结篇
    • 业务中处理数据结构常用的JS方法
    • 作用域
    • 你真的掌握变量和类型了吗
    • 前后端分离之数据Mock
    • 原型与原型链
    • 原生JS补给(上)
    • 如何写出一个惊艳面试官的深拷贝
    • 带你填一些JS容易出错的坑
    • 彻底弄懂 JavaScript 执行机制
    • 执行上下文 执行栈
    • 正则回顾总结
    • 正则基础知识
    • 正则完整篇
    • 正则表达式
    • 浅析Promise原理
    • 浅谈JavaScript中的异步处理
    • 深拷贝 vs 浅拷贝
    • 聊一聊typeof instanceof 实现原理.
    • 聊一聊闭包
    • 高阶函数map reduce filter
  • JavaScript Part 2

  • CSS

  • HTML

  • Jquery

  • ES6

  • 小程序

  • Vue

  • React

  • 深入React

  • React Native

  • NodeJS

  • Angular

  • TypeScript

  • Webpack

  • 浏览器

  • 移动端

  • 前端工程化

  • Electron

  • HTTP

  • Nginx

  • Linux

  • 数据结构与算法

  • LeetCode算法题

  • 综合

完整面试题地址:
作者:程序员poetry
扫码关注作者公众号:「前端进阶之旅」 每天分享技术干货
前端进阶之旅公众号二维码

比继承家业还要简单的JS继承题_封装篇|博客系列

# JS继承系列介绍

看过文章的小伙伴应该都感觉的出,我比较喜欢针对每个知识点出一些比较细节的题目,然后将这些细节连串起来最后组合成大家最爱的综合题😄。

该系列主要为了让我们彻底理解JavaScript面向对象的三大特性:封装、继承、多态。

"咦~这三大特性我知道啊,大清都完了你还在这谈"

啊~ 看到这里你先别着溜,开始的我也是和你一样觉得背背概念,写点小例子就懂了,直到自己给自己出了几道魔鬼题,我才发现之前对它们的理解还是不太全面…因此才有了本系列。

系列总目录:

  • 封装

    • ES6之前的封装-构造函数
    • ES6之后的封装-class
  • 继承

    • 原型链继承
    • 构造继承
    • 组合继承
    • 寄生组合继承
    • 原型式继承
    • class中的extends继承
  • 多态

(在开始写之前本想要一篇文章全部搞定的,但是发现字真太多了,所以才分开来写,而且我终于又可以用我最爱的绯红主题了 😄)

这一章节主要是想向大家介绍一下JS面向对象的第一大特性-封装,也是为了给后面最重要的继承打好基础。

题目也不太多,总共17道,算是牛刀小试吧。

通过阅读本章节你可以学习到:

  • ES6之前的封装-构造函数
  • ES6之后的封装-class

# 前期准备

先来理解一些最最最基本的概念:

(一)

// 1. 构造函数
function Cat (name) {
    this.name
}
// 2. 构造函数原型对象
Cat.prototype
// 3. 使用Cat构造函数创建的实例'乖乖'
var guaiguai = new Cat('guaiguai')
// 4. 构造函数的静态方法,名为fn
Cat.fn = function () {}
// 5. 原型对象上的方法,名为fn
Cat.prototype.fn = function () {}
@前端进阶之旅: 代码已经复制到剪贴板

(二)

语法糖的意思是现有技术本可以实现,但是采用某种写法会更加简洁优雅。

比如class就是语法糖。

(三)

原型链继承的思维导图

(这个暂时看不懂没关系,在继承那一章节中会讲到)

# 封装

把客观事物封装成抽象的类,隐藏属性和方法,仅对外公开接口。

# 1. ES6之前的封装

(虽然以下内容是概念部分,但是对你解题很有帮助哦,请务必仔细阅读它 😁)

都知道ES6的class实际就是一个语法糖,那么在ES6之前,是没有类这个概念的,因此是借助于原型对象和构造函数来实现。

  • 私有属性和方法:只能在构造函数内访问不能被外部所访问(在构造函数内使用var声明的属性)
  • 公有属性和方法(或实例方法):对象外可以访问到对象内的属性和方法(在构造函数内使用this设置,或者设置在构造函数原型对象上比如Cat.prototype.xxx)
  • 静态属性和方法:定义在构造函数上的方法(比如Cat.xxx),不需要实例就可以调用(例如Object.assign())

# 1.1 题目一

(理解私有属性方法和公有属性方法)

比如我现在想要封装一个生产出猫,名为Cat的构造函数。

  • 由于猫的心和胃都是我们肉眼看不见的,所以我把它们设置为私有属性(隐藏起来)
  • 并且猫的心跳我们也是看不到的,所以我把它设置为私有方法(隐藏起来)
  • 然后猫的毛色是可以看见的,所以我把它设置为公有属性
  • 并且猫跳起来这个动作我们是看的到的,所以我把它设置为公有方法
function Cat (name, color) {
  var heart = '❤️'
  var stomach = '胃'
  var heartbeat = function () {
    console.log(heart + '跳')
  }

  this.name = name
  this.color = color

  this.jump = function () {
    heartbeat() // 能跳起来表明这只猫是活的,心也就能跳
    console.log('我跳起来了~来追我啊')
  }
}
var guaiguai = new Cat('guaiguai', 'white')
console.log(guaiguai)
guaiguai.jump()
@前端进阶之旅: 代码已经复制到剪贴板

上述代码打印出来的应该是:

Cat{ name: 'guaiguai', color: 'white', jump: function(){} }
❤️跳
我跳起来了~来追我啊
@前端进阶之旅: 代码已经复制到剪贴板

可以看到,我们生产出名字叫做乖乖的小猫咪只有这几个属性能访问到(也就是能被肉眼看到),为公有属性:

  • name
  • color
  • jump

而私有属性,是我们看不到的:

  • heart
  • somach
  • heartbeat

所以如果你想要直接使用它是不能够的:

// 私有
console.log(guaiguai.heart) // undefined
console.log(guaiguai.stomach) // undefined
guaiguai.heartbeat() // 报错
@前端进阶之旅: 代码已经复制到剪贴板

小结:

很好区分:

  • 在函数内用var定义的就是私有的
  • 在函数内用this承接的就是公有

# 1.2 题目二

(理解静态属性方法和公有属性方法)

我们现在往刚刚的Cat构造函数中加些东西。

  • 我们需要对Cat这个构造函数加一个描述,表明它是用来生产猫的,所以我把descript设置为它的静态属性

  • 由于一听到猫这种动物就觉得它会卖萌,所以我把卖萌这个动作设置为它的静态方法

  • 由于猫都会用唾液清洁身体,所以我把清洁身体这个动作设置为它的公有方法

// 这段是旧代码
function Cat (name, color) {
  var heart = '❤️'
  var stomach = '胃'
  var heartbeat = function () {
    console.log(heart + '跳')
  }

  this.name = name
  this.color = color

  this.jump = function () {
    heartbeat() // 能跳起来表明这只猫是活的,心也就能跳
    console.log('我跳起来了~来追我啊')
  }
}
// 这段是新增的代码
Cat.descript = '我这个构造函数是用来生产出一只猫的'
Cat.actingCute = function () {
  console.log('一听到猫我就想到了它会卖萌')
}
Cat.prototype.cleanTheBody = function () {
  console.log('我会用唾液清洁身体')
}
var guaiguai = new Cat('guaiguai', 'white')

console.log(Cat.descript)
Cat.actingCute()
console.log(guaiguai.descript)
guaiguai.cleanTheBody()
@前端进阶之旅: 代码已经复制到剪贴板

上述代码打印出来的应该是:

'我这个构造函数是用来生产出一只猫的'
'一听到猫我就想到了它会卖萌'
undefined
'我会用唾液清洁身体'
@前端进阶之旅: 代码已经复制到剪贴板

可以看到,我们定义的descript和actingCute是定义在构造函数Cat上的,所以可以直接被Cat调用,为静态属性和方法。

但是descript和actingCute并不能存在于乖乖这个实例上,descript只是对构造函数Cat的描述,并不是对乖乖的描述,所以打印出undefined。

不过清洁身体是定义在原型对象prototype中的,属于公有方法(实例方法),也就是乖乖这个实例可以用它来调用。

静态属性和方法:

  • descript
  • actingCute

实例(公有)属性和方法:

  • name
  • color
  • jump
  • cleanTheBody

小结:

也很好区分:

  • 在构造函数上也就是使用Cat.xxx定义的是静态属性和方法
  • 在构造函数内使用this设置,或者设置在构造函数原型对象上比如Cat.prototype.xxx,就是公有属性和方法(实例方法)

(也有小伙伴可能会有疑问,这个静态属性和方法是有什么用的啊,感觉我们编码的时候并没有用到过啊。Really? 哈哈 😄,Promise.all()、Promise.race()、Object.assign()、Array.form()这些不就是吗?)

(至于实例方法,想想push、shift,它们实际上不是存在于原型对象上的吗?Array.prototype.push)

# 1.3 题目三

(理解实例自身的属性和定义在构造函数原型对象中的属性的区别)

OK👌你刚刚既然已经说了使用this.xxx = xxx的方式和使用Cat.prototype.xxx = xxx都是属于实例对象guaiguai上的公有属性,那它们是有什么区别吗?

来看这道题我们就能理解它们的区别了:

function Cat (name) {
  this.name = name
}
Cat.prototype.prototypeProp = '我是构造函数原型对象上的属性'
Cat.prototype.cleanTheBody = function () {
  console.log('我会用唾液清洁身体')
}
var guaiguai = new Cat('guaiguai')
console.log(guaiguai)
console.log(guaiguai.name)
console.log(guaiguai.prototypeProp)
guaiguai.cleanTheBody()
@前端进阶之旅: 代码已经复制到剪贴板

这里输出的结果 🤔️?

Cat {name: "guaiguai"}
'guaiguai'
'我是构造函数原型对象上的属性'
'我会用唾液清洁身体'
@前端进阶之旅: 代码已经复制到剪贴板

看到没,name是使用this.xxx = xxx的形式定义的,它能直接让实例guaiguai就拥有这个属性。

而prototypeProp 、cleanTheBody毕竟是定义在构造函数原型上的,所以并不能出现在实例guaiguai上,但是guaiguai却能访问和调用它们。

因此我们得出结论:

定义在构造函数原型对象上的属性和方法虽然不能直接表现在实例对象上,但是实例对象却可以访问或者调用它们

# 1.4 题目四

既然我们已经知道了实例自身的属性和定义在构造函数原型对象中的属性的区别,那么我们一般是如何区分它们的呢?

来看看这里:

function Cat (name) {
  this.name = name
}
Cat.prototype.prototypeProp = '我是构造函数原型对象上的属性'
Cat.prototype.cleanTheBody = function () {
  console.log('我会用唾液清洁身体')
}
var guaiguai = new Cat('guaiguai')

for (key in guaiguai) {
  if (guaiguai.hasOwnProperty(key)) {
    console.log('我是自身属性', key)
  } else {
    console.log('我不是自身属性', key)
  }
}
console.log('-分隔符-')
console.log(Object.keys(guaiguai))
console.log(Object.getOwnPropertyNames(guaiguai))
@前端进阶之旅: 代码已经复制到剪贴板

这道题中,我分别用了三种方式来获取实例对象guaiguai上的属性名:

  • for...in...
  • Object.keys()
  • Object.getOwnPropertyNames()

输出的结果为:

'我是自身属性 name'
'我不是自身属性 prototypeProp'
'我不是自身属性 cleanTheBody'
'-分隔符-'
["name"]
["name"]
@前端进阶之旅: 代码已经复制到剪贴板

← 深浅拷贝问点BFC →

fe
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片NEW
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
  • JavaScript Part 1

    • Ajax总结篇
    • Canvas 绘制八大行星
    • DOM编程之API学习总结篇
    • JavaScript事件机制
    • JavaScript代码片段100个
    • JavaScript作用域分析总结
    • JavaScript原型链回顾
    • JavaScript及jQuery中的各种宽高属性图解
    • JavaScript工程项目的一系列最佳实践
    • JavaScript常用API合集
    • JavaScript数组、字符串、对象常用方法
    • JavaScript数组方法总结篇
    • JavaScript深浅拷贝
    • JavaScript运动框架之速度时间版本
    • JavaScript运行机制Event Loop
    • JavaScript防抖节流原理
    • Javascript中的复制粘贴功能
    • Javascript数组详解
    • OOP之原型与原型链
    • Object.defineProperty详解
    • V8源码浅析JS数组常见方法
    • await 在 forEach 中不生效解决方案
    • iframe+表单跨域提交POST请求
    • javascript笔记总结篇
    • 业务中处理数据结构常用的JS方法
    • 作用域
    • 你真的掌握变量和类型了吗
    • 前后端分离之数据Mock
    • 原型与原型链
    • 原生JS补给(上)
    • 如何写出一个惊艳面试官的深拷贝
    • 带你填一些JS容易出错的坑
    • 彻底弄懂 JavaScript 执行机制
    • 执行上下文 执行栈
    • 正则回顾总结
    • 正则基础知识
    • 正则完整篇
    • 正则表达式
    • 浅析Promise原理
    • 浅谈JavaScript中的异步处理
    • 深拷贝 vs 浅拷贝
    • 聊一聊typeof instanceof 实现原理.
    • 聊一聊闭包
    • 高阶函数map reduce filter
  • JavaScript Part 2

  • CSS

  • HTML

  • Jquery

  • ES6

  • 小程序

  • Vue

  • React

  • 深入React

  • React Native

  • NodeJS

  • Angular

  • TypeScript

  • Webpack

  • 浏览器

  • 移动端

  • 前端工程化

  • Electron

  • HTTP

  • Nginx

  • Linux

  • 数据结构与算法

  • LeetCode算法题

  • 综合