这周复习完了 js 基础,整理一波~
一、认识 JavaScript
1. 1 编程语言
- 计算机语言:概念比较广泛,包括 html 标记语言(它并不是编程语言)
- 编程语言特点
- 具有数据和数据结构
- 指令和流程控制:switch,if,while,for
- 引用机制和重用机制
- 包含自己的设计哲学
1.2 组成
- ECMAScript:定义语言规范,实现语言层面的一些语法
- DOM:操作文档的 API
- BOM:操作浏览器的 API
1.3 JS 引擎
- JavaSript 是一门解释型语言
- JS 引擎是 JavaScript 语言的运行解释器
- 浏览器内核中有两种引擎,其中一种就是 JS 引擎
- 排版引擎
- 负责 HTML 和 CSS 解析和排版
- JS 引擎
- 负责解析和运行 JavaScript 语句
- 排版引擎
- 常见 JS 引擎有
- SpiderMonkey -> 第一款 JavaScript 引擎,Brendan Eich 开发
- Chakra -> 微软开发
- WebKit -> JavaScriptCore -> APPLE 开发
- Chrome -> V8 -> GOOGLE 开发
- 小程序 -> JSCore -> 腾讯开发
二、JavaScript 的基本语法
2.1 交互方式
- 弹窗输出
alert("Hello World!")
- 控制台输出
console.log("Hello World!")
- 文档输出
document.write("Hello World!")
- 弹出输入框输入
prompt("Please write some words!")
2.2 noscript
- 普通的一个元素
- 用户将对应的功能关闭不希望它显示的时候或者是浏览器不支持脚本的时候使用
- 将会显示里面的内容
2.3 注意
- script不能写成单标签元素
- 加载顺序:作为HTML文档内容的一部分,JavaScript默认遵循HTML文档的加载顺序,即自上而下的加载顺序
- 推荐将 JavaScript 代码和编写位置放在 body 子元素的最后一行
- JavaScript 代码严格区分大小写
- HTML 元素和 CSS 属性不区分大小写,但是在 JavaScript 中严格区分大小写
三、js 变量和数据类型
3.1 变量
- 命名规则:一个字母、下划线(_ )或一个美元符号($ )
- 命名规范
- 没有声明直接使用会报错
- 有声明没有赋值打印出 undefined
- 不使用 var 默认也是会加入到 window 里面的,不会报错
3.2 数据类型
-
Number:代表数据类型是数值
-
整数
-
浮点
-
数组
-
-
String:代表字符串类型 -> 通常是一段文本
-
Boolean
-
Undefined:变量未定义
-
Null:一个变量准备保存一个对象,但是这个对象不确定时,可以先赋值为 null
-
Object(复杂类型)
-
BigInt:大整数类型,用于任意长度的整数
-
Symbol: 用于唯一的标识符
3.3 typeof 操作符
3.3.1 概念
- 用来获取一个变量的类型
- 不是函数
3.3.2 获取结果
- undefined:值未定义
- boolean
- string
- number
- object:null或对象
- function:函数
- symbol:符号
3.4 Number类型
<script>// 1. Number类型的基本使用:不区分整数类型和浮点数类型var age = 19var height = 1.99// 2. 特殊的数值:// Ininity:无穷大var num1 = Infinityvar num2 = 1/0console.log(num1,num2);// NaN:not a number(不是一个数字)计算错误得到的结果var result = 3* "abc"// 3. 进制表示var num3 = 100//十进制var num4 = 0x100//十六进制var num5 = 0o100//八进制var num6 = 0b100//二进制// 4. 数字表示的范围var max = Number.MAX_VALUEconsole.log(max);var min = Number.MIN_VALUEconsole.log(min);// 5. isNaN// 判断是否不是一个数字</script>
3.5 String 类型
<script>// 1. String基本使用:单引号、双引号、反引号(ES6新增)var name = "lili"// 2. 转义字符:\// 3. 本身属性和方法var message = "helloworld"console.log(message.length);// 字符串操作var nickName = "code"var info = "hah"console.log(nickName + info);</script>
3.6 Boolean 类型
<script>// 1,. Boolean的基本使用var isLogin = truevar isAdmin = false</script>
3.7 Undefined 类型
- 很少主动使用它
<script>var infoconsole.log(info);// 不规范的操作:定义变量没有进行初始化,默认就是undefined// 定义一个变量需要进行初始化:0,空字符串,null</script>
3.8 Object 类型
- 原始类型:值只包含一个单独的内容
- 复杂类型:表示一组数据,是其他数据的一个集合
<script>// Object类型var person = {name: "lili",height: 1.88,age: 99}console.log(person);console.log(person.name);
</script>
- 将对象变成不可变数据(不能追加更改):
Object.freeze(obj)
- 只会冻结最外层
- 深层次的并不能更改
3.9 Null 类型
- 一个变量准备保存一个对象,但是这个对象不确定时,可以先赋值为null
<script>// Null类型var age = 0//Number类型初始值var isAdmin = false//Boolean类型初始值var message = ""//空字符串// 以上都转化为falsevar books = {}//truevar book = null//建议对象初始化为nullconsole.log(book);//null// 存在意义:对对象进行初始化console.log(typeof null);//Object</script>
3.10 数据类型转换
3.10.1 String 的转换
<script>var num = 123var age = 18var isAdmin = false// 一、隐式转换// 1. 一个字符串和其他类型进行+操作var numStr = num1 + ""var ageStr = age + ""var isAdminStr = isAdmin + ""console.log(numStr, ageStr, isAdminStr);console.log(typeof numStr, typeof ageStr, typeof isAdminStr);// 2. 某些函数的执行会自动将参数转化为字符串console.log(num);// 二、显式转换:String()函数、toString()var numStr1 = String(num)</script>
3.10.2 Number 的转换
<script>// 一、隐式转换var num1 = "1234"var num2 = "9"var result1 = num1 + num2console.log(result);// *:转化为数字var result2 = num1 * num2console.log(result2);// 二、显式转换:Number()var result3 = Number(num1)// 其他类型转化为数字类型// 1. undefined:NaNconsole.log(Number(undefined)); //NaN// 2.null:内存地址,本来说是0,其实是falseconsole.log(Number(null));// 3.true:1 false:0console.log(Number(true));console.log(Number(false));// 4.string: // 前后有很多空格会去除空格,如果剩余字符串为空转化成0// 只包含数字:给你转化成数字// 不是数字:NaN</script>
3.10.3 boolean 的转换
<script>// 隐式转换// 分支语句var isAdmin = truevar num1 = 123 //true// 先将其他类型转化成boolean类型,进行语句判断// 这里是隐式转换// 隐式转换有如下规则:// 0、空字符串""、undefined、null、NaN---->false// 其他值--->falseif (num1) {}// 显式转换:Boolean()console.log("");//falseconsole.log("0")//trueconsole.log(Boolean(num1));</script>
四、JavaScript基础运算符
4.1 总结
运算符 | 运算规则 | 范例 | 结果 |
---|---|---|---|
+ | 加法 | 2 + 3 | 5 |
+ | 连接字符串 | ‘中’+‘国’ | ‘中国’ |
_ | 减法 | 2 - 3 | -1 |
* | 乘法 | 2 * 3 | 6 |
/ | 除法 | 5 / 2 | 2.5 |
% | 取余 | 5 % 2 | 1 |
** | 幂 | 2 ** 3 | 8 |
== | 相等 | 4 == 3 | false |
!= | 不相等 | 4 != 3 | true |
> | 大于 | 4 > 3 | true |
< | 小于 | 4 < 3 | false |
>= | 大于等于 | 4 <= 3 | false |
<= | 小于等于 | 4 >= 3 | false |
4.2 运算符
- 也称为操作符
- 划分为很多类型
- 算术运算符
- 赋值运算符
- 关系运算符(比较运算符)
- 逻辑运算符
4.3 运算元
- 运算符作用的对象
- 一元运算符
-
- 二元运算符
- 三元运算符
4.4 算术运算符
-
- 加法
- 连接字符串
- -:减法
- *:乘法
- /:除法
- %:取余
- **:幂(ES7)
- ** === Math.pow( , )
4.5 赋值运算符
- =
- 链式赋值:从右到左进行计算,所有的变量共享一个值
4.6 原地修改
- 对一个变量操作并将新的值存储到变量中
- +=
- -=
4.7 自增自减
- ++
- –
- 只能用于变量上面
- 放前放后的区别
- 前置
- 后置
4.8 比较运算符
- == VS ===
- ==:在类型不相同的情况下,会将运算元转化成Number类型的值再进行比较,是一种隐式转化
- NaN==NaN->false
- undefined == NaN->true
- null 的特殊性
- ===:在类型不相同的情况下,直接返回 false
- ==:在类型不相同的情况下,会将运算元转化成Number类型的值再进行比较,是一种隐式转化
- Object.is()
- VS ==:不会进行隐式转换
- VS===:对待有符号的零和 NaN 不同
- +0 和-0 false
- NaN 和 NaN true
五、JavaScript 分支语句
5.1 if else if else
<script>// 如果条件成立, 那么执行代码块// 如果条件不成立, 执行另一段代码块if (条件判断) {// 条件成立, 执行代码块} else {// 条件不成立, 执行代码块}if (条件1) {// 代码块1} else if (条件2) {// 代码块2} else if (条件3) {// 代码块3} else {// 代码块4(所有条件不成立)}switch (变量) {case 常量1:// 语句一breakcase 常量2:// 语句二breakdefault:// 语句三}</script>
5.2 程序的执行顺序
- 顺序
- 分支
- 判断是否登录
- 循环
5.3 代码块
- 放在{ }里面
<script>// 大括号的作用:对象/代码块// 代码块:// 结合流程控制是否执行代码块{var num1 = 3var num2 = 9var num3 = 98}// 对象不是代码块var info = {name: "lili",age: 72}</script>
5.4 分支结构
- 判断结构或者选择结构
5.4.1 单分支
- if
- 关键字
- 数字0、空字符串、“”、null、undefined、NaN==》false===falsy假值
- 其他值==》true===truthy真值
5.4.2 多分支
- if else
5.5 逻辑运算符
5.5.1 ||
- 逻辑或
- 可以单独使用
- 本质
- 从左到右依次运算运算元
- 先将运算元转成布尔类型
- 对转成的布尔类型进行判断
- true–》直接返回原结果
- false–》一直往后找
- 如果找到最后也没有找到,返回最后一个运算元
- 可以获取第一个有值的结果
<script>var info = undefinedinfo = "lili"var message = info || "我是默认值"console.log(message.length);</script>
5.5.2 &&
- 逻辑与
- 本质
- 遇到false直接返回运算元
- 所有都为true返回最后一个运算元
- 可以对对象中的方法进行有值判断
// 逻辑与的本质
var obj = {name: "lili",friend: {name: "xiejiamiao",eating: function () {console.log("想成为一个喜欢干饭的人~~");}}
}
obj.friend.eating();
// 最严谨的写法
obj&&obj.friend&&obj.friend.eating&&obj.friend.eating()
5.5.3 !
- 逻辑非
- !!==》转换成布尔类型
5.6 switch语句
- 只能做值的相等判断
- case穿透----》需要break
- 严格相等
六、JavaScript循环语句
6.1 循环
- while 循环
- 不确定循环几次
- do…while 循环
- for 循环
- 确定循环次数
- 开发常用
6.2 循环控制关键字
- break
- continue
<script>// break:直接跳出循环var names= ["aaa","bbb","ccc","ddd"]for(var i = 0;i<4;i++){console.log(names[i]);if(names[i]==="ccc"){break}}// continue:立刻结束本次循环,不执行下面的代码var names= ["aaa","bbb","ccc","ddd"]for(var i = 0;i<4;i++){if(names[i]==="ccc"){continue}console.log(names[i]);}</script>
6.3 猜数字游戏
<script>// 0~1的随机数// Math.random():[0,1)for (var i = 0; i < 1000; i++) {// 得到0~99的数字var randomNum = Math.random() * 100randomNum = Math.floor(randomNum)console.log(randomNum);}// 生成一个0~99的随机数var num = Math.floor(Math.random() * 100)var isSuccess = falsevar count = 7for (var i = 0; i < count; i++) {var inputNum = Number(prompt("请输入您猜的数字"))if (inputNum === num) {alert("恭喜您猜对了");break} else if (inputNum > num) {alert("猜大了");} else {alert("猜小了");}if (count - 1 === i) {alert("您的机会已经用完了!");}}if (!isSuccess) {alert("您的机会已经用完了!");}</script>
七、JavaScript函数
7.1 局部变量和全局变量
- 局部变量:定义在函数里面的遍历
- 全局变量:在函数以外声明的变量
- 优先访问自己作用域中的变量,如果没有找到,向父作用域继续查找,直到找到为止,如果找到 window 还没有找到,会报错
7.2 foo 等名词
- foo
- baz
- bar
7.3 函数
- 定义:某种特定功能的代码的封装
- alert
- prompt
- console.log
- String/Number/Boolean
7.4 使用步骤
- 声明函数:封装 独立的功能
- 也称为函数的定义
- 对某些功能的封装过程
- 调用函数:享受 封装的成果
- 也称为函数调用
- 让已存在的函数为我们所用
- 函数作用:使用函数可以提高编写的效率以及代码的重用,类似于一个工具,更加灵活一点
7.5 声明和调用函数
7.5.1 声明函数
- 使用function关键字,称为函数的定义
- 函数代码块不会默认执行
7.5.2 调用函数
- test()
7.6 函数的参数
- 增加了函数的通用性,针对相同的出局处理逻辑,能够适应更多的数据
- 形参:变量使用
- 实参:函数内部使用
7.7 函数的返回值
- 没有return语句,默认返回undefined
- 只有return,返回undefined
- 函数执行过程遇到return关键字,函数会立即停止执行,退出函数
7.8 函数的练习
<script>// 1. 获取面积function getRectangleArea(width, height) {return width * height}var area1 = getRectangleArea(20, 30)console.log(area1);function getCircleArea(radius) {return Math.PI * radius * radius}var area2 = getCircleArea(88)console.log(area2);// 2. 计算1~n的和function sumN(n) {if(n<=0)returnvar sum = 0for (var i = 1; i <= n; i++) {sum += i}return sum}var sumN=sumN(99)console.log(sumN);</script>
7.9 argument 参数
-
函数有一个特别的对象:argument对象
-
是所有非箭头函数中都可以使用的局部变量
-
该对象中包含了所有传入的参数
-
是一个类数组对象
-
形参数量不够接收,但是所有实参都放在了 arguments 对象之中
7.10 函数递归
- 必须要有递归终止条件,不然会产生无限调用的情况
- 无限调用栈内存会被耗尽最后报错
- 递归:函数调用自己
7.11 实现 x 的 y 次幂
<script>function foo() {console.log("foo~~~");}// 递归案例// 方式一:es6function pow1(x, y) {return x ** y}// 方式二:Math.pow实现function pow2(x, y) {return Math.pow(x, y)}// 方式三:for循环function pow3(x, y) {var result = 1for (var i = 0; i < y; i++) {result *= x}return result}// 方式四:递归实现// 性能比较低:占用过多的栈内存// 优点:写出来的代码非常简洁function pow4(x, y) {if(y===1) return xreturn x * pow4(x, y - 1)}</script>
7.12 斐波那契
// 递归案例之斐波那契
function fibonacci(n) {if (n === 1 || n === 2) return 1return fibonacci(n - 1) + fibonacci(n - 2)
}// for循环实现斐波那契
function fibonacci(n) {if (n === 1 || n === 2) return 1var n1 = 1var n2 = 1var result = 0for (var i = 3; i <= n; i++) {result = n1 + n2n1 = n2n2 = result}return result
}
7.13 局部变量和外部变量
- 在 es5 之前没有块级作用域
- 在 es5 之前没有块级作用域的概念,但是函数可以定义自己的作用域
- 作用域:表示一些标识符的作用有效返回
- 函数的作用域:在函数内部定义的变量,只有在函数内部可以被访问到
<script>//1. message的作用域: message在哪一个范围内可以被使用// 全局变量:全局作用域var message = "hellolili"if (true) {console.log(message);}function foo() {console.log("在foo中访问message:", message);}foo()// 2. es5之前没有块级作用域(var 定义的变量没有块级作用域){count = 100console.log("在代码块中访问count:", count);}console.log("在代码块外面访问count:", count);// for循环的代码块叶没有自己的作用域for (var i = 0; i < 3; i++) {var foo = "foo"}console.log("for循坏外面访问foo:", foo);console.log("for循环外面访问i:", i);//3// 3. es5之前函数代码块会形成自己的作用域// 意味着在函数内部定义的变量外面是访问不到的function test() {var bar = "bar"}test()// console.log("在test外面访问bar:", bar);// 函数有自己的作用域:函数内部定义的变量只有函数内部能访问到function sayHello() {var nickname = "kobe"console.log("在sayHello内部访问nickName:", nickname);function sayHi(){console.log("hiFunction!exec......");console.log("在hi函数中访问nickname:",nickname);}sayHi()}sayHello()// console.log("在sayHello外面访问nickName:", nickname);</script>
- 局部变量:定义在函数内部的变量
- 外部变量:定义在函数外部的变量
<script>// 打印undefinedconsole.log(message);// 全局变量(global variable):在script中定义一个变量,哪哪都可以使用var message = "message"// 局部变量(local variable):在函数内部定义的变量只有在函数内部才能进行访问,定义在函数内部的变量// 外部变量(outer variable):在函数内部去访问函数之外的变量,这个变量就是外部变量</script>
- 通过var声明的变量会添加到window里面
7.14 变量的查找顺序
- 全局变量中没有找到
- 会在 window 中查找有没有这个属性
7.15 定义函数的方式
-
函数的声明:使用 function 定义
-
函数表达式: var bar = function(){}
<script>//函数的声明function foo(){}// 函数表达式:允许省略函数名字var bar = function(){console.log("bar函数被执行了~~");}</script>
- 对比
- 语法不同
- 函数声明:是在主代码流中声明为单独的语句的函数
- 函数表达式:在一个表达式中或另一个语法结构中 创建的函数
- js 创建的时机不同
- 函数声明:被定义之前就可以被调用
- 函数表达式:在代码执行到达时才会被创建
- 语法不同
- js 脚本中的全局函数声明会先被js引擎进行解析,并创建,是内部算法的缘故,所以函数声明可以放在函数调动之后
- 一般使用函数声明,更加灵活
7.16 JavaScript 头等函数
- 头等函数:在程序设计语言中,函数被当做头等公民
- 函数可以作为别的函数的参数、函数的返回值、赋值给变量或者存储在数据结构中
- 函数式编程:使用函数来作为头等公民使用函数,这种编程方式(范式)叫做函数式编程
- 最早来自于 lisp 这门语言
<script>// 函数作为头等公民// 1. 函数可以赋值给变量(函数表达式写法)// 匿名函数赋值给 foo1var foo1 = function () {console.log("foo1函数被执行");}foo1()// 2. 让函数在变量之间来回传递var foo2 = foo1foo2()// 3. 函数可以作为另外一个函数的参数function bar(fn) {console.log("fn:", fn);}bar(foo1)// 4. 函数作为另外一个函数的返回值function sayHello(name) {function hi() {console.log(" hi " + name);}return hi}var fn = sayHello("lili")//函数柯里化fn()// 5. 将函数存储在另外一个数据结构中var obj = {name: "lili",eating: function () {console.log("eating~~");}}obj.eating()function bar1() {console.log("bar1");}function bar2() {console.log("bar2");}function bar3() {console.log("bar3");}// 事件总线的封装var fns = [bar1, bar2, bar3]</script>
7.17 回调函数
- 函数可以传递给另外一个函数
- 高阶函数:一个函数可以接收一个或多个函数作为输入(参数 )或者返回一个函数
- 匿名函数:传入函数的时候没有指定名称或者函数表达式指定函数对应的变量
<script>// 1. 函数回调的概念理解function foo(fn) {// 函数的回调:通过fn去调用bar函数的过程fn()console.log("foo执行~~");}function bar() {console.log("bar函数执行");}foo(bar)// 2. 函数回调的案例function request(url, callback) {console.log("根据URL向服务器发送网络请求");console.log("需要花费比较长的事件拿到对应的结果");var list = ["javascript", "java", "python"]callback(list)}function handleResult(res) {console.log("在handleResult中拿到结果", res);}request("http://baidu.com", handleResult)// 3. 案例重构:一般这样应用function request(url, callback) {console.log("根据URL向服务器发送网络请求");console.log("需要花费比较长的事件拿到对应的结果");var list = ["javascript", "java", "python"]callback(list)}// 传入的函数是没有名字的,成为匿名函数request("http://baidu.com", function (res) {console.log("在handleResult中拿到结果", res);})</script>
7.18 立即执行函数
- 一个函数定义完后被立即执行
- 定义一个匿名函数,这个函数有自己独立的作用域
- ()表示这个函数被执行了
- IIFE
<script>// 定义函数,定义完这个函数之后,会要求这个函数立即被执行// ():前面加一个小括号表示一个整体(function () {console.log("函数立刻被执行!!!");})();// 注意!!前面的语句要加一个 分号// {}代码块/对象类型// ()控制优先级/函数的调用/函数的参数// []定义一个数组/从数组/对象中取值/对象的计算属性// 定义完之后立即执行可以不写名字// 可以传入参数、可以有返回值,跟普通的函数是一样的(function () {console.log("立即执行函数的调用");})()</script>
- 和普通的代码的区别
- 立即执行函数中定义的变量是有自己的作用域的
- 应用:最大特点,形成自己的作用域
- 防止全局变量的命名冲突(早期开发都会包裹),产生自己的作用域----之后使用esmodule
var xmModule = (function () {var xmModule = {}var message = "hello lilei";console.log(message);var nickname = "nickname是啥";console.log(nickname);console.log(nickname.length);xmModule.message = messagereturn xmModule
})();
(function () {console.log(xmModule.message.length);
})();
- 获取所有的按钮监听点击
<!-- 应用二案例 --><button class="btn">按钮一</button><button class="btn">按钮二</button><button class="btn">按钮三</button><button class="btn">按钮四</button><script>// 1. 获取一个按钮监听点击// 1. 拿到html元素// var btnEl = document.querySelector(".btn")// console.log(btnEl);// // 2. 监听对应按钮的点击// btnEl.onclick = function(){// console.log("点击按钮一")// }// 2. 获取所有的按钮监听点击var btnEls = document.querySelectorAll(".btn")for (var i = 0; i < btnEls.length; i++) {var btn = btnEls[i];(function (m) {btn.onclick = function () {// 使用var,i是在上层作用域找到的-->全局console.log(`按钮${m + 1}发生了点击`);}})(i)}(function (m) {console.log(m);})(0)</script>
- 其他写法
// 常见写法
(function () {console.log("立即执行函数被调用!");
})()// 错误写法:函数声明不能立即调用// function foo(){// 这里的小括号当成了优先级// }()// 其他写法(function () { })
//类似于以下的写法,类似于函数表达式
function foo(fn) {}
foo(function () {})// 让匿名函数直接执行(function (fn) {}());
// +号让其变成表达式
+function foo(){}()
7.19 代码风格
// 1. foo和()之间不需要有空格// 2. 多参数,后面加上一个空格// 3. 小括号和大括号之间有一个空格// 4. {和其他函数定义定义在同一行
7.20 Chrome 的 debug 调试
- sources
- 查看源代码
- 打断点
- 直接在html中写debugger
- 在源代码中打断点
八、JavaScript面向对象
面向对象:研究对象之间的关系
8.1 对象类型
- 是一种存储键值对的数据类型
- 原始类型只能存储简单的值
- 键值对可以是属性和方法
- key是字符串,在定义对象的属性名时,大部分情况下引号都是可以省略的
- value可以是任意值
<script>/*函数(function):通过function默认定义一个结构方法(method):将一个函数放到对象中,作为对象的一个属性*/var person = {name: "lili",age: 18,height: 1.63,run: function () {console.log("这是一个喜欢跑步的人~~~");},study: function () {console.log("期末考来临不得不学习");}}</script>
8.2 创建和使用对象
- 对象字面量:{}
- new Object + 动态添加属性
- new 其他类
<script>/*函数(function):通过function默认定义一个结构方法(method):将一个函数放到对象中,作为对象的一个属性*/// 定义了一个对象var person = {name: "lili",age: 18,height: 1.63,run: function () {console.log("这是一个喜欢跑步的人~~~");},study: function () {console.log("期末考来临不得不学习");},"my friend":{name:"jiamiao"}}// 访问/修改/添加对象的属性console.log(person.name);person.name="lili"person.study = function(){console.log("学~~~");}// 删除属性delete person.height</script>
8.3 方括号和引用的使用
<script>var obj = {name: "lili","my friend": "kobe","eating something": function () {console.log("eating~~~");}}console.log(obj["my friend"]);console.log(obj.name === obj["name"]);var eatingKey = "eating something"obj["eating something"]()// 也还是要用[]进行使用obj[eatingKey]()</script>
8.4 对象的遍历
<script>var info = {name: "lili",age: 12,height: 1.88}// 对对象进行遍历// 1. 普通for循环:要用到索引值i// 循环次数的确定:Object.keys(info)返回了一个数组,进而得到数组的长度console.log(Object.keys(info));var infoKeys = Object.keys(info)for (var i = 0; i < infoKeys.length; i++) {var key = infoKeys[i]// key值保存在一个变量里面,不能用.的方式取出来,要用[]var value = info[key]console.log(`key:${key},value:${value}`);}//2. for in :遍历对象,不需要用到索引值ifor (var key in info) {// 一开始就拿到了keyconsole.log(key);var value = info[key]console.log(value);}// 3. for of:默认不能遍历对象,要求遍历对象是一个可迭代对象iterable,支持数组for (var foo of info) {}</script>
8.5 栈内存和堆内存
- 程序都是加载到内存中来执行的,代码由CPU来运行
- 栈stack内存:原始类型
- 变量名仍然放在栈内存,对应的值为对象类型,会指向堆内存中的一个内存地址
- 变量对应的类型是原始类型,直接复制一份给它
- 变量对应的类型是对象类型,保存的是它的内存地址,并没有重新赋值一份:当前的变量是对对象的引用
- 修改对应的值会同步改变,两者指向同一个对象
- 堆heap内存:对象类型
8.6 值类型和引用类型
- 原始类型的保存方式:在变量中保存的是值本身
- 原始类型也被称为值类型
- 对象类型的保存方式:在变量中保存的是对象的引用
- 对象类型也被称为引用类型
- 值类型(原始类型):在变量中保存的是值本身,占据的空间是在栈内存中分配的
- 引用类型(对象类型):在变量中保存的是对象的“引用”,占据的空间是在堆内存中分配的
- 将值类型传递给函数参数,函数内部对参数的改变不会影响函数外部的变量
- 将引用类型传递给函数参数,函数参数保存的是对象的"引用",在函数内部修改对象的属性会影
8.7 deepThink
<script>var num1 = 123var num2 = 123console.log(num1 === num2);//true// 1. 两个对象的比较var obj1 = {}var obj2 = {}console.log(obj1 === obj2);//false// 2. 引用的赋值:会直接修改内存中的值var info = {name: "lili",// 只要是对象类型就会重新开辟一个内存空间friend: {name: "kebe"}}var friend = info.friendfriend.name = "james"console.log(info.friend.name);//james// 3. 值传递和引用传递的区别// 3.1值传递function foo(a) {a = 321}var num = 100// 函数执行完会被销毁foo(num)console.log(num);//100// 3.2引用传递:但是在函数中创建了一个新对象,没有对传入对象进行修改function bar(a) {// 变量a创建了一个新的内存地址去保存对象a = {name: "lili"}}var obj = { name: "obj" }// 将对象传进去bar(obj)// obj从始至终没有改变过console.log(obj.name);// 3.3引用传递:但是对传入的对象进行修改var obj = {name: "obj"}function baz(a) {a.name = "lili"}baz(obj)console.log(obj.name);</script>
8.8 this指向
- 理解
<script>// 普通函数中都有一个this变量:在大多数情况下会指向一个对象// arguments:保存的是传入的所有参数// 1. 情况一:普通函数被默认调用,this指向windowfunction foo(name, age) {console.log(arguments);console.log(this);//window对象}foo("lili", 123)function sayHello() {console.log(this);}sayHello()// 2. 情况二:函数是被某一个对象引用并且调用它,this会指向这个对象(调用的那个对象)var obj = {// 函数没有谁属于谁,看谁调用的,谁就是thisrunning: function () {console.log("I LIKE RUNNING");console.log(this === obj);}}obj.running()//true// 将running这个函数赋值给fn// 引用的赋值// 题目一:var fn = obj.runningfn()//window// 题目二:function bar() {console.log("bar function~~");console.log(this);}var person = {name: "lili",bar: bar}person.bar()//person</script>
- 开发中作用:方便在一个方法拿到当前对象的一些属性
<script>// 对象没有作用域,只是一种数据类型var info = {name: "lili",running: function () {console.log(this.name + " is running");},eating: function () {console.log(this + " is eating");},studying: function () {console.log(this + " is studying");}}info.running()</script>
8.9 类和对象
- 创建一系列对象
- 编写重复代码
- for 循环
- 工厂函数:一种设计模式,一种设计方案
<script>// 一系列的学生对象// 重复代码的复用var stu1 = {name: "lili",age: 19,height: 1.88,running: function () {console.log("running~~");}}var stu2 = {name: "kobe",age: 29,height: 1.98,running: function () {console.log("running~~");}}var stu3 = {name: "jully",age: 23,height: 1.48,running: function () {console.log("running~~");}}</script>
<script>// for循环for (var i = 0; i < 3; i++) {var stu = {name: "lili",age: 19,height: 1.88,running: function () {console.log("running~~");}}}// 工厂函数:生产student对象//通过工厂设计模式,自己来定义了一个这样的函数function createStudent(name, age) {var stu = {}stu.name = namestu.age = agestu.running = function () {console.log("runnning");}return stu}var stu1 = createStudent("lili", 34)</script>
- 应用:开发中创建一系列相似的对象
8.10 JavaScript 中的类(ES5)
- 使用 new 操作符
- 一个函数使用new操作符调用了,会执行以下操作
- 在内存中创建了一个新的对象(空对象)
- 函数内部的 this ,会指向创建出来的新对象
- 执行函数的内部代码
- 函数没有明确的返回一个非空对象,this 指向的新对象会自动返回
<script>// JavaScript已经默认提供给了我们可以更加符合JavaScript思维方式的一种创建对象的规则// 思维方式:面向对象/*如果一个函数被new操作符调用:1. 创建出来一个新的空对象2. 将空对象赋值给this(让this指向这个空对象)3. 执行函数体代码块4. 如果没有明确的返回一个非空对象,那么这个this指向的对象会自动返回*/function Student(name, age, height) {// 在函数中的this一般指向某一个对象this.name = namethis.age = agethis.height = heightthis.running = function () {console.log("runnning");}}// 在函数调用的前面加new操作符(关键字)var stu1 = new Student("lili", 34, 1.33)var stu2 = new Student("jiao", 34, 22)console.log(stu1);console.log(stu2);</script>
- 从表现形式上来说和千千万万个普通函数没有任何区别
- 但是这个普通函数使用new操作符调用,就是一个构造函数
8.11 构造函数
- 通过工厂函数打印出来的都是Object类型,我们希望打印出Student类型
- 构造函数也称之为构造器,通常在创建对象时会调用的函数,用于创建对象
- 它扮演了在其他语言中 类 的角色
- 这样设计就是一种语言的缺陷:function声明函数/构造函数
- 所以出现了ES6开始出现了class
- 但是es6还是被转换成es5在浏览器中运行,因为有一些浏览器不支持es6
8.12 类和对象的关系
- 类:图纸的描述,一类事物的统称
- 对象:根据类创建的实体对象,具体的对象
- 面向对象编程:拥有类的思维,之后再使用new创建对象
- JavaScript:支持多范式编程
- 应用
- react 组件化开发
- 之前都是用 class,现在使用 hook,function
- 封装成类:axios
- react 组件化开发
- 类意味着构造函数:DOM类
8.13 全局对象
- 浏览器中存在一个全局对象object - > window
- 作用:
- 查找变量时,最终会找到window身上
- 将一些浏览器全局提供给我们的变量/函数/对象,放在window对象上面
- alert()、console.log()、
- Chrome浏览器展示的只是为了方便我们调试
- 使用var定义的变量会被添加到window上面(正在被抛弃)
- 浏览器发现后面才添加变量到window上面,但是他会对打印内容进行更新
8.14 函数也是对象
- function是一个对象类型,使用函数表达式给变量赋值时候函数对象是保存在堆内存里面的
- var foo = function(){} === var foo = new Function()
<script>//定义原始类型的变量//地址、指针、引用var name = "lili"var age = 19// 定义对象类型的变量var obj = {}var foo = function(){}//堆内存function bar(){} //堆内存console.log(typeof obj);//object// 规范有规定,函数返回function,但是它属于object,它只是返回更加具体的类型console.log(typeof bar);//function// 举例:学生属于人是吧!!!// 本质:Function继承Objectfunction sayHello(){}// 对象可以往里面放属性sayHello.age =19function Dog(){}// 对象可以往里面放函数// 构造函数上(类上面)添加的函数,称之为类方法Dog.running = function(){}</script>
8.15 new 操作原理
- new 操作的原理
- 在内存中创建一个空对象 ----比如 var moni={}
- 将构造函数的显示原型赋值给这个对象的隐式原型 ----moni.proto = Person.prototype
- this 指向创建出来的新对象 —this = moni
- 执行函数体代码
- 如果构造函数没有返回非空对象,那自动返回创建出来的新对象 —return moni
- 全局对象的作用
- 查找变量时,最终会找到 window
- 将浏览器全局提供给我们的变量/函数/对象 ,放在 window 对象上面 ,比如 alert,console 等
- 使用var 定义的变量会添加到 window 上
- 函数也是对象