文章目录
20.【JS全解】JavaScript 概览
20.1 视频:学JS的基本要求
- 软要求
- 逻辑能力
- 质疑自己的能力
不要相信人类, 包括你自己
通过double check可以缓解 - 抽象思维
高级程序员必备能力
- 硬要求
- 足够的代码量
如何统计自己的代码行数- 安装
yarn global add cloc
统计该文件夹代码(cloc = count line of code)
cloc --vcs=git .
- 注意
如何仓库有node_modules目录等不相关内容
需要吧/node_modules/写入 .gitignore文件
- 安装
- 了解足够多的概念
- 常用常考
闭包, 原型
类, 继承
MVC, Flux
高级函数
前端工程化 - 如何积累
在课程中提炼
在大脑中思考
在博客上总结
在代码中实践
- 常用常考
- 有足够的采坑经验
- 何为专家
把该领域内所有的错误都犯完的人, 就是专家 - 如何踩坑
做项目, 而且是个人项目
个人项目的意思是所有代码都是你一个人写的
这样才可以全方位踩坑
- 何为专家
- 足够的代码量
20.2 视频:JS的历史
JS之父-布兰登(Brendan Eich)
ECMAScript标准的制定:
- 1997年6月, 第一版ECMAScript发布
- 1999年12月, 第三版发布, 这个版本使用最广
- 第四版, 流产.
- 2009年12月, 第五版发布, 增加了一些功能
- 2015年6月, 第六版发布, 新浏览器都支持这一版
- 之后每年发布一版, 版本号以年份命名
JS与ECMAScript的关系
- ECMAScript是纸上的标准, JS是浏览器的实现.
- 纸上标准往往落后浏览器, 先实现, 再写进标准
JS的兴起:
- 2004年愚人节, 谷歌发布Gmail在线网页
- 2005年, jesse将谷歌用到的技术命名为AJAX
- 2006年, jQuery发布(很好的兼容的IE浏览器 ), 是 目前最长的JS库, 直到IE不行了, 稍微不火.
20.3 视频:中国前端的发展
前端的来源:
- 一部分来自自学的后端程序员, 他们把Java的思想代入JavaScript, 面向对象成了JS的主流思想.
- 一部分来自设计师, 他们开始学习CSS, 并独创了 「重构工程师」岗位(现已没落)
缺人:
- 大学没有推出相关课程
- 由于早期前端工资比不上后端, 所有大部分人选后者
- 高材生选择机器学习
- 一不小心进入前端领域科班生成长迅速
V8快如闪电:
- Chrome的JS引擎叫做V8(V1到V7是其他语言的引擎)
- 2009年, Ryan基于V8创建了Node.js
- 2010年, Isaac基于Node.js写出了npm
总结:
-
JS是历史的选择:
- 一开始浏览器支持很多东西:Java, Flash, VBScript
- 只有JS活到最后
-
JS的低开高走:
- 一开始JS就是一个玩具语言
- 但是JS每次都走对了风口
-
学习JS时需注意:
- 旧的, 过时的东西了解就好, 考前记忆一些
- 跟IE相关的知识一律忽略
21.【JS全解】内存图与JS世界(精品课)
21.1 视频:进程与线程
一切都在内存中运行
-
开机:
- 操作系统在c盘里(macOS在根目录下多个目录里)
- 按下开机键, 主板通电, 开始读取固件(固件就是固定在主板上的存储设备, 里面有开机程序)
- 开机程序将文件里的操作系统加载到内存中运行
-
操作系统(以Linux为例)
- 首先加载操作系统内核
- 然后启动初始化进程, 编号为1, 每个进程都有编号.
- 启动系统服务: 文件, 安全, 联网
- 等待用户登录:输入密码登录/ssh登录
- 登录后, 运行shell, 用户就可以和操作系统对话
- bash也是一种shell, 图形化界面可认为是一种shell
-
chrome.exe
- 双击Chrome图标, 就会运行chrome.exe文件
- 开启Chrome进程, 作为主进程
- 主进程会开启一些辅助进程, 如网络服务, GPU加速
- 你每创建一个网页, 就有可能会开启一个子进程
-
浏览器的功能
- 发起请求, 下载HTML, 解析HTML, 下载CSS, 解析CSS, 渲染界面, 下载JS, 解析JS, 执行JS等
- 功能模块: 用户界面, 渲染引擎, JS引擎, 存储等(一般各自处于不同的线程)
21.2 视频:JS引擎
JS引擎:
- Chrome用的是V8引擎, C++编写的
- 网景用的是
- spiderMonkey, 后被Firefox使用, C++
- Safari用的是JavaScriptCore
- IE用的是 Chakra(JScript9)
- Edge用的是Chakra(JavaScript), 现在内核用的是V8.
- Node.js用的是V8引擎
JS引擎的主要功能:
- 编译: 把JS代码翻译为机器能执行的字节码或机器吗
- 优化:改写代码, 使其更高效
- 执行:执行上面的字节码或机器码
- 垃圾回收: 把JS用完的内存回收, 方便之后再次使用
21.3 视频:瓜分内存
准备工作:
- 提供API:window, document, setTimeout(这些不是JS自身具备的功能)
- 以上的API称为运行环境runtime env
JS运行在内存中
红色区域的作用:
- 红色区域用来存放数据
- 红色区域并不存放变量名, 变量名在「不知什么区」
- 每一种浏览器的分配规则并不一样
- 上图的区域并不完整, 没有画调用栈, 任务队列等区域
Stack和Heap
- 红色区域分为Stack栈和Heap堆
- Stack区特点: 每次数据顺序存放
- Heap区特点: 每次数据随机存放
规律:
- 数据分为两种: 非对象(数字, 字符串, boolen)和对象
- 非对象都存放在Stack
- 对象都存放在Heap
- =号总是会把右边的东西复制到左边(不存在什么传值和传地址)
21.4 视频:JS入门三座大山之原型
JS的世界需要的前提:
- window
以下的都是挂到window, 挂在window上可以在任何地方直接用- console
- document
- object
var person = new Object()
等价于var person = {}
(前者正规写法, 后者方便好用) - array
var a = new Array(1, 2, 3)等价于var a = [1, 2, 3]
(同上) - function
var f = new Function()
等价于function f(){}
(同上)
更简单的画法(灰色部分是不存在的,首字母大写的, 一般都有prototype属性):
window细节:
- window变量和window对象是两个东西
- window变量是一个容器, 存在window对象的地址
- window对象是Heap里的一坨数据
同理:
- console和console对象不是同一个东西
- Object和Object函数对象不是同一个东西
- 前者是内存地址, 后者是一坨内存
21.5 视频:JS世界
以下代码为什么可以运行, 不报错?
var obj = {}
obj.toString()
obj有一个隐藏属性, 隐藏属性存储了Object.prototype对象的地址, obj.toString()发现obj上没有toString, 就去隐藏属性对应的对象里面找, 于是就找到了Object.prototype.toString
原型让你无需重复声明共用属性
每一个对象都有一个隐藏属性(指向原型(对象))
隐藏属性: __proto__
暂时只理解:
- 每个大写字母开头的对象只关心prototype
- 小写开头的对象关心
__proto__
21.6 视频:犀利提问
22.【JS全解】Canvas 实践—画图板
22.1 视频:画点
用div来模仿简单画图工具, 但是用JS来操作HTML性能低下.
<!DOCTYPE html>
<html lang="ch-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="canvas"></div>
<script>
canvas.onmousemove = (e) => {
console.log(e)
console.log(e.clientX)
console.log(e.clientY)
let div = document.createElement('div')
div.style.position = 'absolute'
div.style.left = e.clientX + 'px'
div.style.top = e.clientY + 'px'
div.style.width = '6px'
div.style.height = '6px'
div.style.marginLeft = "-3px"
div.style.marginTop = "-3px"
div.style.borderRadius = '50%'
div.style.background = 'black'
canvas.appendChild(div)
}
</script>
</body>
</html>
* {
border: 0;
padding: 0;
box-sizing: border-box;
}
#canvas {
height: 100vh;
border: 1px solid red;
}
22.2 视频:画线
使用canvas来模仿简单画图工具
@@@canvas类似image,可以直接设置宽高, 但是会被CSS覆盖@@@
@@@canvas必须直接设置好宽高, 不然css覆盖后会不清晰@@@
@@@canvas默认display:inline@@@
<!DOCTYPE html>
<html lang="ch-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<canvas id="canvas" width="100" height="100"></canvas>
<script>
function drawLine(x1, y1, x2, y2) {
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
}
let canvas = document.getElementById('canvas');
// 获取当前浏览器的宽高
canvas.width = document.documentElement.clientWidth
canvas.height = document.documentElement.clientHeight
// 画线
let ctx = canvas.getContext('2d');
// 图形的填充颜色
ctx.fillStyle = 'green';
// 图形轮廓的颜色
ctx.strokeStyle = 'green'
ctx.lineWidth = '30'
// 将线的两头变圆
ctx.lineCap = "round";
//开关
let painting = false;
// 记录上一次的clientX, clientY
let last
// 按下开始画
canvas.onmousedown = (e) => {
painting = true
last = [e.clientX, e.clientY]
}
canvas.onmousemove = (e) => {
if (painting === true) {
drawLine(last[0], last[1], e.clientX, e.clientY)
last = [e.clientX, e.clientY]
}
}
// 松开停止画
canvas.onmouseup = (e) => {
painting = false
}
</script>
</body>
</html>
* {
border: 0;
padding: 0;
/* 这里我和方方老师不一样的地方, body多了margin:8px, 则我这里初始化一下margin为0, 可达到刷新页面没有滚动条效果*/
margin: 0;
box-sizing: border-box;
}
#canvas {
display: block;
}
使用canvas来模仿简单画图工具(兼顾手机端)
<!DOCTYPE html>
<html lang="ch-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<canvas id="canvas" width="100" height="100"></canvas>
<script>
function drawLine(x1, y1, x2, y2) {
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
}
let canvas = document.getElementById('canvas');
// 获取当前浏览器的宽高
canvas.width = document.documentElement.clientWidth
canvas.height = document.documentElement.clientHeight
// 画线
let ctx = canvas.getContext('2d');
// 图形的填充颜色
ctx.fillStyle = 'black';
// 图形轮廓的颜色
ctx.strokeStyle = 'black'
ctx.lineWidth = '4'
// 将线的两头变圆
ctx.lineCap = "round";
// 记录上一次的clientX, clientY
let last
// 判断是手机还是PC
var isTouchDevice = 'ontouchstart' in document.documentElement;
if (isTouchDevice) {
canvas.ontouchstart = (e) => {
let x = e.touches[0].clientX
let y = e.touches[0].clientY
last = [x, y]
}
canvas.ontouchmove = (e) => {
let x = e.touches[0].clientX
let y = e.touches[0].clientY
drawLine(last[0], last[1], x, y)
last = [x, y]
}
} else {
//开关
let painting = false;
// 按下开始画
canvas.onmousedown = (e) => {
painting = true
last = [e.clientX, e.clientY]
}
canvas.onmousemove = (e) => {
if (painting === true) {
drawLine(last[0], last[1], e.clientX, e.clientY)
last = [e.clientX, e.clientY]
}
}
// 松开停止画
canvas.onmouseup = (e) => {
painting = false
}
}
</script>
</body>
</html>
* {
border: 0;
padding: 0;
/* 这里我和方方老师不一样的地方, body多了margin:8px, 则我这里初始化一下margin为0, 可达到刷新页面没有滚动条效果*/
margin: 0;
box-sizing: border-box;
}
#canvas {
display: block;
}
23.【JS全解】JS语法
23.1 视频:JS语言评价
JS版本:
- ES3, IE6支持, 总体评价:垃圾
- ES5, 总体评价:还是垃圾
- ES6, 大部分浏览器支持, 总体评价:一半垃圾(兼容旧版本:稳定), 一半好
- ES 2019与ES6差别不大
一门语言的价值:由其产生的价值决定
- JS是世界上使用最广的语言
- JS是门槛极低的语言(只要你不学糟粕)
- JS是一门能产生生产价值的语言(虽然不美)
JSON之父-道格拉斯.克罗克福特(Douglas Crockford)
23.2 视频:表达式, 语句, 标识符
表达式:
- 1 + 2表达式的值为3
- add(1+2)表达式的值为函数的返回值
- console.log表达式的值为函数本身
- @@@
console.log(3)表达式的值为:undefined
@@面试题@
语句: var a = 1 是一个语句
表达式与语句的不同:
- 表达式一般都有值, 语句可能有
- 语句一般会改变环境(变量, 赋值)
- 以上两句话并不是绝对的
大小写敏感
空格大部分没有实际意义
回车:
@@@
只有return后不能加空格,不然返回值就是默认为undefined
@@面试题@
标识符:
- Unicode字母(不仅仅是英文字母)
- $
- _
- 中文
- 以上四种开头都行
注释:
- 不好的注释:
- 把代码翻译成中文
没必要, 可能会隐藏关键的注释 - 过时的注释
注释中有一些时长改变的值, 有事会忘记改, 从而可能会导致一些影响. - 发泄不满的注释
程序员要有自己的素养, 发泄情绪不要在职业上.
- 把代码翻译成中文
- 好的注释:
- 踩坑的注释
描述踩坑, 怎么解决这个坑等, 方便日后的接手的程序员方便阅读 - 为什么代码会写的这么奇怪
因某种原因, 这部分代码不符合常规, 需要描述说明 - 遇到什么bug
遇到bug, 无论解决否都需要注释一下, 方便以后维护, 以及若解决了, 注释上解决方案.
- 踩坑的注释
区块block(常常与if/ for/ while合用):
{
let a = 1
let b = 2
}
23.3 视频:if…else…语句
if(表达式){语句1}else{语句2}
a = 1
if(a === 2)
console.log('a')
console.log('a等于2')
最推荐的写法:
if (表达式) {
语句
} else if (表达式) {
语句
} else {
}
@@@
以上的else if本质上是两个if语句拼接的第一个else部分省略了区域block, 则直接接了另一个if语句, 同时逻辑感觉就是else if.
if (表达式) {
语句
} else
if (表达式) {
语句
} else {
}
@@细节@
次推荐的语句:
function fn(){
if(表达式){
return 表达式
}
if(表达式){
return 表达式
}
return 表达式
}
switch语句:
大部分情况下都需要加break, 设置break的初衷就是为了, 2个case在一个下执行. 一些新的语言如swift就不需要break了, 若存在多种情况直接在case后用,
隔开即可.
三元运算符(问好冒号表达式):表达式1 ? 表达式2 : 表达式3
&&短路逻辑:
- A&&B&&C&&D取第一个假值或者D
- @@@
并不会去true/false
@@细节@ - 用的最多的情况为:fn函数存在
fn && fn()
||短路逻辑:
- A||B||C||D取第一个真值或者D
- @@@
并不会去true/false
@@细节@ - 用的最多的情况为:保底值
A = A || 100
23.4 视频:while, for循环语句
while循环这里Chrome会莫名多输出一个10, 但是下面接一个console.log就没有了.
@@@
while(a !== 1){
console.log(a)
a = a + 0.1
}
这是一个死循环, 原因浮点数
@@面试题@
@@@
加入setTimeout会过一会执行, 则输入的都是5
新语法中用let
替代var
则可以避免这种问题
@@面试题@
label:
{
foo:1
}
24.【JS全解】JS数据类型
24.1 视频:数据类型
- JS中, 数字使用64位浮点数的形式存储的
- JS中, 字符串是用类似UTF8形式存储的(UCS-2)
ASCII码中:
-
0: 48
-
A: 65
-
a: 97
-
GB2312(国标2312):一开始用这个来给汉字编号, 但是只收录了6000多的汉字, 西文字母和日文假字
-
GBK(国标扩): 因GB2312的局限, 微软推出了国标扩展, 收录了21886个汉字和图形符号, 收录了中日韩使用的几乎所有汉字完全兼容GB2312
-
GB18030:后来国标局推出了GB28030, 但是不兼容GB2312, 所以不受欢迎.
-
Unicode: 后来, 又多了藏文, 泰文等,万国码就诞生了, 收录了13万字符(大于16位), 全世界通用(以后还会继续更新), 但是缺点第一, 两个字节不够用, 每个字节要用三个及以上字节, 第二, 所以这样所有文件都扩大50%.
-
UTF-8:最少可用8位存一个字符, 相对Unicode优化了Unicode来带的缺点.
24.2 视频:数字number
写法:
- 整数写法:1
- 小数写法:0.1
- 科学计数法:1.23e4
- 八进制写法(用的少): 0123, 00123, 0o123
- 十六进制写法: 0x3F, 0X3F
- 二进制写法: 0b11, 0B11
特殊值:
- 正0和负0:都等于0, 要严谨
- 无穷大: infinity, +infinity, -infinity
- 无法表示的数字: NaN(Not a Number), 但是是一个数字
64位浮点数:
浮点就是浮动的店, 意思就是小数点会动
123.456可以表示为1.23456e10^2,
也可以表示为12345.6e10/^-2
- 符号占1位
- 指数占11位(-1023~1024)
- 有效数字占52位(开头的1省略)
范围和精度:
- 范围(忽略符号位)
- 最大值
*最小值Number.MAX_VALUE 1.7976931348623157e+308
Number.MIN_VALUE 5e-324
- 精度(有效数字)
2^53: 9007 1992 5474 0992
24.3 视频:字符串String
写法:
- 单引号
‘你好’ - 双引号
“你好” - 反引号(推荐)
`你好`
转义:
- \uFFFF 表示对应的Unicode字符
- \xFF 表示对应的Unicode前256个字符
如果有多个换行现在推荐用反引号:
字符串的属性: - length
- string[index]
base64转码: 一般用于隐藏邮箱
- window.btoa: 正常字符串转为Base64编码的字符串.
- window.atob:Base64编码的字符串转为原来的字符串.
24.4 视频:布尔值boolen
@@@
5个falsy值:
- undefined
- null
- 0
- NaN
''
@@细节@
24.5 视频:两个空undefined和null
JS的原(La)创(Ji)之处
区别(没有本质区别):
- 如果一个变量声明了, 但没有赋值, 那么默认值就是undefined, 而不是null
- 如果一个函数, 没有写return, 那么默认return为undefined, 而不是null
- 前端程序员习惯上, 把非对象的空值写为undefined, 把对象的空值写为null, 但这仅仅只是习惯上.
symbol符号:不怎么常用的数据类型
JS 中的 Symbol 是什么?
24.6 视频:变量声明
三种声明方式:
- var a = 1
- let a = 1
- const a = 1
区别:
- var是过时的(ES3), 不好用的方式
- let是新的, 更合理的方式
- const是声明时必须赋值, 且不能再改的方式
let声明:
-
遵循块作用域, 即使用范围不超过{}
-
不能重复声明
-
可以赋值, 也可以不赋值
-
必须先声明再使用, 否则报错
-
全局声明的let编码, 不会变成window的属性
-
for循环配合let有奇效(for循环部分展示过了)
-
const声明
-
跟let几乎一样
-
唯一不同:声明时就需要赋值, 赋值后不能改
24.7 视频:类型转换
- number -> string
var n = 1
undefined
String(n)
"1"
//通常使用以下这种
n + ''
"1"
- string -> number
var s = '1'
undefined
Number(s)
1
//通常使用以下方式
s - 0
1
+s
1
- x -> bool
var s = 1
undefined
Boolean(s)
true
//通常使用以下这种
!!s
true
x -> string
var s = true
undefined
s.toString()
"true"
1..toString()
"1"
1 .toString()
"1"
(1).toString()
"1"
25.【JS全解】JS 对象
25.1 视频:对象的语法
七种数据类型:
- 4基: number, string, boolen, symbol
- 2空: null, undefined
- 1对象: object
五种falsy值:
- null
- undefined
- 0
- ‘’
- NaN(Not A Number)
对象定义:
- 无序的数据集合
- 键值对的集合
写法:
//常用写法
let obj = {'name': 'zxc', 'age': 23}
//规范写法
let obj2 = new Object({'name': 'zxc', 'age': 23})
//可以直接console.log一个无名对象, 注意:不要和lable混淆了.
console.log({'name': 'zxc', 'age': 23})
{name: "zxc", age: 23}
细节:
- 键名是字符串, 不是标识符, 可以包含任意字符.
- 引号可省略, 省略之后就只能写标识符
- @@@
就算引号省略了, 键名还是字符串
@@细节, 重要@
奇怪的属性名(都是字符串):
变量作为属性名:
以前的写法(变量作为属性名):
对象的隐藏属性:
- 每一个对象都有隐藏属性
- 隐藏属性中存储这共用属性组成的对象的地址
- 共用属性组成的对象叫做原型
- 也就是说, 隐藏属性存储着原型的地址
超纲知识:
除了字符串, symbol也能做属性名
let a = Symbol()
let obj = {[a]: "Hello"}
25.2 视频:删属性
删除属性和查看是否含有该属性名:
不能用obj.xxx === undefined 来判断xxx是否为obj的属性
25.3 视频:查属性
查看自身所有属性名:Object.keys(obj)
查看自身所有属性值:Object.values(obj)
查看自身以及共用属性:console.dir(obj)
推荐这种, obj.__proto__
推荐前面这种
判断一个属性是自身的还是共用的: obj.hasOwnProperty('toString')
原型:
- 原型里面存着对象的共用属性
- 比如obj的原型就是一个对象
obj.__proto__
存着这个对象的地址
原型也是对象:
- 原型也是有原型的
- 原型是对象的跟
- 虽然原型有原型, 但是为null
查看属性:
中括号语法: obj[‘key’]
点语法: obj.key
25.4 视频:写属性
批量赋值:
Object.assign(obj, {age: 23, gender: “men”})
无法通过自身修改或增加共用属性:
let obj = {}, obj = {}//共用toString属性
obj.toString = 'xxx'//只会在改obj自身属性
obj2.toString //还是原型上的
偏要修改或增加原型的属性:一般来说不要修改, 会引起很多问题, 这也是JS的脆弱之处.
obj.__proto__toString = 'xxx' //推荐使用下面这种
Object.prototype.toString = 'xxx'//推荐使用这种
修改隐藏属性:
//不推荐使用__proto__这种
let obj = {name: 'zxc'}
let obj2 = {name: 'cxz'}
let common = {kind: 'human'}
obj.__proto__ = common
obj2.__proto__ = common
//推荐使用这种
//Object.create尽量初始化空对象, 然后再进行追加, 这样方便.
let obj = Object.create(common)
obj.name = 'zxc'
let obj2 = Object.create(common)
obj2.name = 'cxz'
推荐的理由: 要改就一开始就要改, 别后面再改
26.【JS全解】JS 对象分类
26.1 视频:构造函数
输出正方形的面积和长度:
- 利用原型:为了解决每次都开辟新的内存空间, 来使用原型来共用对象的属性.
let squareList = []
let widthList = [5, 6, 5, 6, 5]
let squarePrototype = {
getArea(){
return this.width * this.width
},
getLength(){
return this.width * 4
}
}
for(let i = 0; i<12; i++){
squareList[i] = Object.create(squarePrototype)
squareList[i].width = widthList[i]
}
//创建square的代码太分散
- 抽离到函数:为了解决square太分散, 那就把代码抽离到一个函数里, 然后调用函数
let squareList = []
let widthList = [5, 6, 5, 6, 5]
//此函数叫做构造函数:构造对象的函数就是构造函数
function createSquare(width){
//以squarePrototype为原型创建空对象
let obj = Object.create(squarePrototype)
obj.width = width
return obj
}
let squarePrototype = {
getArea(){
return this.width * this.width
},
getLength(){
return this.width * 4
}
}
for(let i = 0; i<12; i++){
squareList[i] = createSquare(widthList[i])
}
//squarePrototype原型和createSquare函数还是分散的
- 函数和原型的结合:为squarePrototype原型和createSquare函数还是分散的,将两者结合起来,使得更紧密.
let squareList = []
let widthList = [5, 6, 5, 6, 5]
function createSquare(width){
let obj = Object.create(createSquare.squarePrototype)
obj.width = width
return obj
}
//因为函数也是一个对象, 则这里可以直接用点访问形式.
createSquare.squarePrototype = {
getArea(){
return this.width * this.width
},
getLength(){
return this.width * 4
},
//方便通过原型找到构造函数
constructor: createSquare
}
for(let i = 0; i<12; i++){
squareList[i] = createSquare(widthList[i])
//constructor 可以知道谁够着了这个对象
console.log(squareList[i].constructor)
}
- 函数和原型结合(重写): 通过new操作符来重写
let squareList = []
let widthList = [5, 6, 5, 6, 5]
function Square(width){
//JS之父帮你写了这句let obj = Object.create(createSquare.squarePrototype)
this.width = width
//JS之父帮你写了这句return obj
}
//因为函数也是一个对象, 则这里可以直接用点访问形式.
Square.prototype.getArea = {
return this.width * this.width
}
Square.prototype.getLength = {
return this.width * 4
}
for(let i = 0; i<12; i++){
squareList[i] = new Square(widthList[i])
//constructor 可以知道谁够着了这个对象
console.log(squareList[i].constructor)
}
对比:
@@@
总结:
- new X()自动做了四件事情:JS之父的❤️
- 自动创建对象
- 自动为空对象关联原型, 原型地址指定为X.prototype
- 自动将空对象作为this关键字运行构造函数
- 自动return this
- 构造函数X:
- X函数本事负责给对象本身添加属性
- X.prototype对象负责保存对象的共用属性
代码规范:
- 大小写:
- 所有构造函数(专门用于创建对象的函数)
- 所有被构造出来的对象, 首字母小写
- 词性:
- new后面的函数, 使用名词形式: 如new Person(), new Object()
- 其他函数, 一般使用动词开头: 如createSquare(5), createElement(‘div’)
@@细节, 重点@
26.2 视频:原型与公用属性的关系
结论: 你是谁构造的你的原型就是谁的prototype对应的对象
原型公式: 对象.proto === 其构造函数.prototype
参考资料:JS 中 proto 和 prototype 存在的意义是什么?
Object的属性的值为途中右边的window.object值下面的#101, Object:#101起到了一个找到这个对象的作用.
为什么直接打印对象的时候的时候, 是直接打印出来的对象的属性?
@@@
Object.prototype是由Object函数构造出来的.
Object.prototype的原型是: Object.prototype.__proto__ = null
@@细节@
26.3 视频:对象需要分类
26.4 视频:类型 V.S. 类
类型:类型是JS数据的分类, 有7种(4基2空1对象)
类:类是针对对象的分类, 有无数种, 常见的有Array, Funciton, Date, RegExp等
@@@
JS终极一问:
- window是谁构造的: Window
- window.Object是谁构造的:window.Function(所有的函数都是window.Function构造的)
- window.Function 是谁构造的:window.Funciton(通过上帝(浏览器)构造了Function, 然后自己构造自己)
@@细节@
26.5 视频:class语法
class现在是主流, 但是prototype也不过时, 角度不一样:我为什么一定要学 prototype?
ES6新语法class:跟方方老师学, 老师教的都是常用的
class Square{
constructor(width){
this.width = width
}
//函数的两种写法都对
//新语法
getArea(){
return this.width * this.width
}
//旧语法
getLength: fucntion(){
return this.width * 4
}
}
类和对象的新语法:
27.【JS全解】JS数组
27.1 视频:数组是什么
数组对象是一种特殊的对象, JS其实没有真正的数组(只是用对象模拟数组)
典型的数组:
- 元素的数据类型相同
- 使用连续的内存存储
- 通过数字下表获取元素
JS的数组:
- 元素的数据类型可以不同
- 内存不一定是连续的(对象是随机存储的)
- 不能通过数字下表, 而是通过字符串下标
创建一个数组:
- 新建
let arr = [1, 2, 3]
let arr = new Array(1, 2, 3,)
//创建长度为3的数组
let arr = new Array(3)
- 转化
let arr = '1, 2, 3'.split(',')
let arr = '123'.split('')
//字符串和带length的数组可以一下方式转化为数组
Array.from('123')
Array.from({0:'a', 1:'b', length:2})
- 伪数组
伪数组的原型链中并没有数组的原型
<div>1</div>
<div>2</div>
<div>3</div>
<script>
//获取出来的不是数组
let divList = document.querySelectorAll('div')
//需要转换为数组
let divArr = Array.from(divList)
console.log(divArr)
</script>
@@@
没有数组共有属性的「数组」就是伪数组
@@重点, 细节@
- 合并两个数组,的到一个新数组
arr1.concat(arr2)
- 截取一个数组的一部分
@@@
JS只提供浅拷贝
@@细节@
//从第二个元素开始
arr1.slice(1)
//全部截取(可以拷贝一个数组)
arr1.slice(0)
27.2 视频:删除数组中的元素
-
跟对象一样:
数组的长度并没有变, 稀疏数组 -
直接修改length可以删元素:
不要随便改length长度
以上两种都不是删数组元素的方法.
删数组元素:
- 删除头部的元素
arr.shift()
, arr被修改, 并返回被删除的元素 - 删除尾部的元素
arr.pop()
, arr被修改, 并返回被删除的元素 - 删除中间的元素
//删除index的一个元素, 1表示删除长度
arr.splice(index, 1)
//并在删除位置添加'x'
arr.splice(index, 1, 'x')
//并在删除位置添加'x', 'y'
arr.splice(index, 1, 'x', 'y')
27.3 视频:遍历数组
查看所有属性名:
let arr=[1, 2,3 ,4, 5]
arr.x = 'xxx'
Object.keys(arr)
for(let i in arr){
console.log(`${i}: ${arr[i]}`)
}
@@@
不用以上方法来操作数组, 因为数组是索引是下标0到length-1的数
@@细节@
查看数字(字符串)属性名和值
- for循环
- forEach
arr.forEach(function(item, index, array){
console.log(`${index}: ${item}`)
})
自己实现forEach
function forEach(array, fn){
for(let i = 0; i<array.length; i++){
fn(array[i], i, array)
}
}
forEach([1, 2, 3], function(value, key){
console.log(key, value)
})
查看单个属性:
- 索引越界
arr[arr.length] === undefined
arr[-1] === undefined
- 查看某个元素是否在数组里
arr.indexOf(item)
, 存在返回索引, 否则返回-1 - 使用条件查找元素
arr.find(item=> item%2 === 0)
, 找到第一个偶数, 否则返回undefined - 使用条件查找元素的索引
arr.findIndex(item=> item%2 === 0)
, 找到第一个偶数索引, 否则返回-1
27.4 视频:增加数组中的元素
@@@
不实用上面直接通过索引来添加数组元素.
@@细节@
- 在尾部加元素
//修改arr, 返回新长度
arr.push(newItem)
//修改arr, 返回新长度
arr.push(item1,item2)
- 在头部加元素
//修改arr, 返回新长度
arr.unshift(newItem)
//修改arr, 返回新长度
arr.unshift(item1,item2)
- 在中间添加元素
//在index处插入'x'
arr.splice(index, 0, 'x')
arr.splice(index, 0, 'x', 'y')
- 反转顺序
arr.reverse()
, 修改原数组 - 自定义顺序
arr.sort()
默认是从小到大排序
如果要求从大到小排序呢?
可以使用sort和reverse来实现
但是我就是要用sort来实现呢
原始代码:
//1的话数字从左边开始, -1的话数字从右边开始
arr.sort(function(a, b){
if(a > b){
return 1
}else if(a === b){
return 0
}else{
return -1
}
})
简写代码:
arr.sort((a, b)=> a-b)
27.5 视频:数组变换
- map: n变n
- filter:n变少
- reduce:n变1
通过reduce来实现平方
通过reduce来实现过滤奇数
法一:
法二:用三元运算符
法三:通过拼接一个空的[]
28.【JS全解】JS 函数
28.1 视频:四种方式定义函数
@@@
JS中函数是对象
@@细节@
- 具名函数
function 函数名(形式参数1, 形式参数2){
语句
return 返回值
}
-
匿名函数:具名函数去掉函数名就是匿名函数
let a = function(x, y){return x+y}
function(x, y){return x+y}
部分叫做函数表达式 -
箭头函数
let f1 = x => x*x
let f2 = (x,y) => x+y
let f3 = (x,y) => {return x+y}
@@@
如果要返回一个对象
let f4 = (x, y) => ({name:x, age:y})
@@JS并不是一门好语言@
- 用构造函数
let f = new Function('x', 'y', 'return x+y')
基本没人用, 但是能让你知道函数是谁构造出来的
@@@
所有函数都是Function构造出来的
包括: Object, Array, Function
@@细节@
let fn = () => console.log('hi')
let fn2 = fn
fn2()
fn保存了匿名函数的地址
这个地址被复制给了fn2
fn2()调用了匿名函数
fn和fn2都是匿名函数的引用而已
真正的函数既不是fn也不是fn2
28.2 视频:调用时机
刻舟求剑
- 例5:
let a = 1
function fn(){
setTimeout(()=>{
console.log(a)
}, 0)
}
fn()
a = 2
打印结果为2
类似, 你正在打LOL, 马上最后一波互拆家的时候, 你妈(fn())叫你吃饭, 你是马上, 然后你是先把对面家拆了然后再去吃饭.
- 例6
let i = 0
for(i = 0; i<6; i++){
setTimeout(()=>{
console.log(i)
}, 0)
}
答案:6个6
当setTimeout()的毫秒数设置为0的时候,是要先执行完函数调用栈中的代码,然后立即调用定时器。
因为定时器都被放在了一个队列中,等待上下文的可执行代码运行完毕后,才开始运行定时器,也就是定时器才刚开始计时。代码中声明一个let全局变量i,然后在for循环中改变i,所以在定时器的方法执行的时候,变量i已经变成了6,所以输出的全部是6。
这里的互拆家是for循环, 先要把这最后一波打完(循环结束), 此时的i的值为6, 然后再执行6个需要执行的setTimeout打印出6.
- 例7
因为例6不符合, 新人思路, 然后JS就’聪明的’用例7来实现新人的想法
for(let i = 0; i<6; i++){
setTimeout(()=>{
console.log(i)
}, 0)
}
答案:0, 1, 2, 3, ,4, 5
@@@
因为JS在for 和let一起用的时候回加东西, 保留每次循环i的值
每次循环会多创建一个i
@@JS并不是一门好语言@
28.3 视频:作用域 & 闭包
JS中是静态作用域(词法作用域)
JS的全局变量:
- 在顶级作用域声明的变量
- window的属性(无论是不是在顶级作用域定义)
作用域原则: 就近原则
闭包:
28.4 视频:参数和返回值
形式参数的意思就是非实际参数:
function add(x, y){
return x+y
}
add(1, 2)
其中x和y就是形参, 因为并不是实际的参数
调用add时, 1和2是实际参数, 会被赋值给x和y
上面声明add函数代码等价于
function add(){
var x = arguments[0]
var y = arguments[1]
return x + y
}
@@@
JS中形参可多可少
@@JS并不是一门好语言@
@@@
只有函数有返回值, 每一个函数都有返回值
console.log的返回值是undefined
@@细节@
28.5 视频:调用, 调用栈与爆栈
调用栈:JS引擎在调用一个函数前, 需要吧函数所在的环境push到一个数组里, 这个数组叫做调用栈,等函数执行完了, 就会把环境弹pop出来, 然后return到之前的环境, 继续执行后续代码.
递归: 先递进, 后回归
爆栈:
function computeMaxCallStackSize(){
try{
return 1 + computeMaxCallStackSize();
}catch(e){
return 1;
}
}
chrome最多可用的栈:
函数提升:
function fn(){}
不管你把函数什么放在哪, 它都会跑到第一行
不是函数提升:
let fn=function(){}
这是赋值, 右边的匿名函数声明不会提升
28.6 视频:person.sayHi()的this
@@@
每一个函数的都有arguments和this, 除了箭头函数
@@细节@
当不传参数的时候, this打印出来的就是window
@@@
传arguments通过直接调用函数fn(), fn(1, 2, 3)那么arguments就是[1, 2, 3]伪数组
可通过Array.from
转为数组
@@细节@
@@@
传this, 通过fn.call(xxx, 1, 2, 3)传入this和arguments
而且xxx会被自动转化为对象
@@JS并不是一门好语言@
方方总结:this是隐藏参数, arguments是普通参数
假设没有this
let person = {
name: 'bens',
sayHi(){
console.log(`你好, 我叫` + person.name)
}
}
可以直接保存了对象地址的变量获取’name’, 这种方法简称为引用
需要一种办法拿到对象, 这样才能获取对象的name属性
python就用了这种办法:
person.sayHi()会隐式地把person作为this传给sayHi, 方便sayHi获取person对应的对象
总结:
- 我们想让函数获取对象的引用
- 但是并不想通过变量名做到
- Python通过额外的self参数做到
- JS通过额外的this 做到:person.sayHI()会把person自动传给sayHi, sayHi可以通过this引用person
小白调法:
person.sayHi(), 会自动把perosn 传到函数里, 作为this
大师调用法(以后都用这种方法, 清晰明了):
person.sayHI.call(person)
, 需要自己手动把person传到函数里, 作为this
28.7 视频:call指定this
function add(x, y){
return x + y
}
没有用到this:
add.call(undefined, 1, 2)
this的两种使用方法:
- 隐式传递
fn(1, 2) //等价于fn.call(undefined, 1, 2)
obj.child.fn(1)//等价于obj.child.fn.call(obj.child, 1)
- 显式传递
fn.call(undefined, 1, 2)
fn.apply(1, [1, 2])
继续实现array.forEach(用this的版本)
function(fn){
for(let i = 0; i<this.length; i++){
fn(this[i], i)
}
}
绑定this:
function f1(p1, p2){
console.log(this, p1, p2)
}
let f2 = f1.bind({name:'bens'})//那么f2就是f1绑定this之后的函数
f2()//等价于f1.call({name: 'bens'})
还可以绑定其他参数
function f1(p1, p2){
console.log(this, p1, p2)
}
let f3 = f1.bind({name:'bens'}, 'hi')
f3()//等价于f1.call({name: 'bens'}, 'hi')
28.8 视频:箭头函数
箭头函数:没有arguments和this
里面的this就是外面的this
28.9 视频:立即执行函数
ES5时代, 为了得到局部变量, 必须引入一个函数, 这个函数有名字, 就得不偿失, 于是这个函数为匿名函数.然后加()
执行它,这种语法不合法, 则只能在前面加!
匿名函数直接执行, 前面加!最好
29.【JS全解】JS 实战,会动的代码
29.1 视频:搞清基本原理
29.2 视频:显示一段话
通过yarn来安装parcel(1.12.4)
yarn global add parcel
通过parcel来运行
parcel src/basics.html
会动的代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>会动的代码</title>
</head>
<body>
<style id="style"></style>
<span id="demo"></span>
<script src="main.js"></script>
</body>
</html>
@@@
每x毫秒计时器动一次是setInterval(), 但是不建议用, 推荐使用setTimeout递归.
@@老手建议@
let html = document.querySelector('#demo')
let style = document.querySelector('#style')
let n = 0
let string = `
/*一二三四五
上山打老虎
老虎没达到
打到小松鼠*/
body{
color:red;
}
`
let string2 = ``
let step = () => {
setTimeout(() => {
if (string[n] === '\n') {
n !== 0 ? string2 += '<br>' : string2
} else if (string[n] === ' ') {
string2 += ' '
} else {
string2 += string[n]
}
html.innerHTML = string2
style.innerHTML = string.substring(0, n)
console.log(string[n])
if (n < string.length - 1) {
n += 1
step()
}
}, 100)
}
step()
29.3 视频:制作太极
通过parcel来运行
parcel src/taiji.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>会动的代码</title>
</head>
<body>
<style id="style"></style>
<span id="demo"></span>
<div id="div1"></div>
<style>
#div1 {
position: fixed;
top: 20px;
right: 20px;
}
#div1::before {
content: '';
display: block;
position: absolute;
}
#div1::after {
content: '';
display: block;
position: absolute;
}
</style>
<script src="main.js"></script>
</body>
</html>
let html = document.querySelector('#demo')
let style = document.querySelector('#style')
let n = 0
let string = `
/*一二三四五
上山打老虎
老虎没达到
打到小松鼠*/
#div1{
border: 1px solid red;
width: 400px;
height: 400px;
}
/* 接下来我把, div 变成一个圆
注意看好了
首先, 把div变成一个圆*/
#div1{
border-radius: 50%;
box-shadow: 0 0 3px rgba(0, 0, 0, 0.5);
border: none;
}
/* 八卦是阴阳形成的
一黑一白(css gradient background generator)*/
#div1{
background: linear-gradient(90deg, rgba(255,255,255,1) 0%, rgba(255,255,255,1) 50%, rgba(0,0,0,1) 50%, rgba(0,0,0,1) 100%);
}
#div1::before{
width: 200px;
height: 200px;
background: #000;
top: 0;
left: 50%;
transform: translateX(-50%);
border-radius: 50%;
background: radial-gradient(circle, rgba(255,255,255,1) 0%, rgba(255,255,255,1) 25%, rgba(0,0,0,1) 25%, rgba(0,0,0,1) 100%);
}
#div1::after{
width: 200px;
height: 200px;
background: #fff;
bottom: 0;
left: 50%;
transform: translateX(-50%);
border-radius: 50%;
background: radial-gradient(circle, rgba(0,0,0,1) 0%, rgba(0,0,0,1) 25%, rgba(255,255,255,1) 25%, rgba(255,255,255,1) 100%);
}
`
let string2 = ``
let step = () => {
setTimeout(() => {
if (string[n] === '\n') {
n !== 0 ? string2 += '<br>' : string2
} else if (string[n] === ' ') {
string2 += ' '
} else {
string2 += string[n]
}
html.innerHTML = string2
style.innerHTML = string.substring(0, n)
console.log(string[n])
if (n < string.length - 1) {
n += 1
step()
}
}, 0)
}
step()
29.4 视频:永远都要考虑手机端
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover">
<title>会动的代码</title>
</head>
<body>
<style id="style"></style>
<div id="html"></div>
<div id="div1Wrapper">
<div id="div1"></div>
</div>
<style>
* {
border: 0;
padding: 0;
}
*::before {
box-sizing: border-box;
}
*::after {
box-sizing: border-box;
}
#html {
word-break: break-all;
}
#div1 {
position: fixed;
top: 20px;
right: 20px;
}
#div1::before {
content: '';
display: block;
position: absolute;
}
#div1::after {
content: '';
display: block;
position: absolute;
}
@media (max-width: 500px) {
#html {
height: 50vh;
overflow: auto;
}
#div1Wrapper {
height: 50vh;
}
#div1 {
position: relative;
top: 0;
right: 0;
}
}
</style>
<script src="main.js"></script>
</body>
</html>
let html = document.querySelector('#html')
let style = document.querySelector('#style')
let n = 0
let string = `
/*一二三四五
上山打老虎
老虎没达到
打到小松鼠*/
#div1{
border: 1px solid red;
width: 200px;
height: 200px;
}
/* 接下来我把, div 变成一个圆
注意看好了
首先, 把div变成一个圆*/
#div1{
border-radius: 50%;
box-shadow: 0 0 3px rgba(0, 0, 0, 0.5);
border: none;
}
/* 八卦是阴阳形成的
一黑一白(css gradient background generator)*/
#div1{
background: linear-gradient(90deg, rgba(255,255,255,1) 0%, rgba(255,255,255,1) 50%, rgba(0,0,0,1) 50%, rgba(0,0,0,1) 100%);
}
#div1::before{
width: 100px;
height: 100px;
background: #000;
top: 0;
left: 50%;
transform: translateX(-50%);
border-radius: 50%;
background: radial-gradient(circle, rgba(255,255,255,1) 0%, rgba(255,255,255,1) 25%, rgba(0,0,0,1) 25%, rgba(0,0,0,1) 100%);
}
#div1::after{
width: 100px;
height: 100px;
background: #fff;
bottom: 0;
left: 50%;
transform: translateX(-50%);
border-radius: 50%;
background: radial-gradient(circle, rgba(0,0,0,1) 0%, rgba(0,0,0,1) 25%, rgba(255,255,255,1) 25%, rgba(255,255,255,1) 100%);
}
`
let string2 = ``
let step = () => {
setTimeout(() => {
if (string[n] === '\n') {
n !== 0 ? string2 += '<br>' : string2
} else if (string[n] === ' ') {
string2 += ' '
} else {
string2 += string[n]
}
html.innerHTML = string2
style.innerHTML = string.substring(0, n)
//PC端滚动条
window.scrollTo(0, 99999);
//mobile滚动
html.scrollTo(0, 99999);
if (n < string.length - 1) {
n += 1
step()
}
}, 0)
}
step()
29.5 视频:部署到GitHub Pages
首先parcel要执行(每次修改后都需要这一步)
parcel build src/index.html --public-url .
然后将代码上传到GitHub
最后效果图
30.【JS全解】JS运算符
30.1 视频:JS运算符
- 算数运算符
number运算:- 指数
x ** 3
- 自增自减: 尽量只在for循环中使用
string运算:
只有+
(连接运算), 没有其他的
不同类型不要加起来 eg:1 + '2'
- 指数
- 比较运算符
>
<
>=
<=
==
!=
===
!==
@@@
NaN !== NaN
@@细节, 特例@
- 布尔运算符
||
&&
!
console&&console.log&&console.log('hi')
, 以防console不存在报错
a = a || 100
, a的保底值, 但是如果是空字符串''
, 也是false. 因为5个false(NaN, undefined, null, 0, ‘’)值都会为假.解决方法, 可以在函数参数赋一个默认值.
- 二进制运算符
@@@
用的很少, 面试的时候看看
@@面试@
|
: 或, 两位都为0, 则结果为0, 否则为1&
: 与, 两位都为1, 则结果为1, 否则为0~
: 否^
: 异或, 两位结果相同, 则结果为0, 否则为1<<
: 左移>>
: 右移>>>
:头部补零的优益运算符(正数情况下, 与右移效果一样)
位运算符的妙用
使用与运算判断奇偶:
偶数 & 1 = 0
奇数 & 1 = 1
使用~
, >>
, <<
, >>>
, |
来取整:
位运算会抹除小数位
console.log(~~ 6.83)
console.log(6.83 >> 0)
console.log(6.83 >>> 0)
console.log(6.83 << 0)
console.log(6.83 | 0)
答案都是6
使用^
来交换a b的值
var a = 5
var b = 8
a ^= b
b ^= a
a ^= b
console.log(a)
console.log(b)
- 其他运算符
点运算符:
- 语法
对象.属性名 = 属性值 - 作用
读取对象的属性值 - 疑问: 不是对象, 为什么也可以有 属性?
'a-b-c'.split('-')
JS有特殊逻辑, 点前面不是对象, 就会把它封装成对象.number, stirng和bool会变成Number, String和Boolean对象.(转成对象, 用对象, 对象消失)
void运算符:
- 语法
void 表达式或语句 - 作用
求表达式的值, 或执行语句, 然后void的值总是为undefined
逗号运算符:
- 语法
表达式1, 表达式2, …, 表达式n - 作用
将表达式n的值作为整体的值 - 使用
let a = (1, 2, 3), 那么a的值就是3.通常用逗号运算符都要搭配括号使用
为了简写, 2句话不用return
let f = (x) => {console.log('平方值为:'); return x*x}
利用逗号运算符可改写为:
let f = (x) => (console.log('平方值为:'), x*x)
30.2 视频:JS运算符优先级
相同运算符:
- 从左到右: a + b +c
- 从右到左: a = b = c =d
@@@
优先级口诀: 元口号优先级最高, 会用圆括号就行了.
@@细节@
31.【JS全解】JavaScript 总结
31.1 视频:拨乱反正
知识点:
- 基本概念
内存, 变量, 数据类型和对象 - 控制语句
if…else
for… - 对象
原型, 原型链
对象分类
new一个新对象
构造函数
this的隐式传递和显式传递
难点:
- JS三座大山
原型
this
AJAX
@@@
-
JS公式
对象.__proto__ === 其构造函数.prototype
JS唯一公式, 如果不会就套公式 -
根公理
Object.prototype是所有对象的(直接或间接)原型
-
函数公理
所有函数都是由Function构造的
任何函数.__proto__ === Function.prototype
任意函数: Object/Array/Function
@@重要的知识@
拨乱反正:
- 乱一
- XXX的原型
{name:'bens'}
的原型
[1, 2, 3]
的原型
Object的原型 - 解读
Object的原型是Object.___proto__
: 对
Object的原型是Object.prototype
: 错 - 错在哪
「的原型」等价于「.__proto__
」
中文的「原型」无法区分__proto__
和prototype
我们约定原型默认表示__proto__
只不过__proto__
正好等于某个函数的prototype
- XXX的原型
- 乱二
- 矛盾
[1, 2, 3]
的原型是Array.prototype
你又说Object.prototype是所有对象的原型
那为什么Object.prototype不是[1, 2, 3]的原型 - 错在哪
原型分两种: 直接原型和间接原型
对于普通对象来说, Object.prototype是直接原型
对于数组, 函数来说, Object.prototype是间接原型
- 矛盾
- 乱三
- Object.prototype不是根对象
- 理由
Object.prototype是所有对象的原型
Object是Function构造出来的
所以, Function构造了Object.prototype
推论, Function才是万物之源
- 错在哪
Object.prototype和Object.prototype对象的区别
对象里面从来都不会包含另一个对象
31.2 视频:再画JS世界
JS世界构造的顺序
- 创建根对象#101(toString), 根对象没有名字
- 创建函数的原型#208(call/apply), 原型__p为#101
- 创建数组的原型#404(push/pop), 原型__p为#101
- 创建Function#342, 原型__p为#208
- 用Function.prototype存储函数的于宁, 等于#208
- 此时发现Function的
__proto__
和prototype都是#208 - 用Function创建Object
- 用Object.prototype存储对象的原型, 等于#101
- 用Function创建Array
- 用Array.prototype存储数组的原型, 等于#404
- 创建window对象
- 用window的Object, Array属性将7和9种的函数命名
- 记住一点, JS创建一个对象时, 不会给这个对象的名字的
31.3 视频:JS三大定理
- Object.prototype的原型
- Function.prototype的原型
var f = ()=>{}
的原型
- Array.prototype.toString的原型
- Object的原型
总结:
- 构造函数
是用来构造对象的
会预先存好对象的原型, 原型的原型是根
new的时候将对象的__p指向原型
31.4 视频:JS学习计划
- 第一阶段
了解JS语法, 特性, 对象, 数组, 函数
- 第二阶段
了解AJAX, 设计模式, 封装, 面向对象, MVC - 第三阶段
Vue/React全家桶