认识jS
JS的组成
- ECMAScript是JavaScript的标准,描述了该语言的语法和基本对象
- JS是ES的语言层面的实现
- 因为除了语言规范之外,JS还需要对页面和浏览器进行各种操作
- 除了基本实现之外,还包括DOM操作和BOM操作
- JS的三个组成
- ESMAScript:定于语言规范
- DOM:操作文档的API
- BOM:操作浏览器的API
JS由谁来运行
- html与css是由浏览器进行解析
- 而JS是由浏览器中的JS引擎来运行的
- JS引擎帮助我们将JS代码翻译成CPU指令来执行
- 比较常见的JS引擎
- SpiderMonkey:第一款JS引擎
- Chakra:微软开发,用于IT浏览器
- JavaScriptCore:webkit中的JS引擎,苹果公司开发
- V8:谷歌开发的强大JS引擎,帮助Chrome从众多浏览器中脱颖而出
JS的基础语法
JS的编写方式
- html的代码内(不推荐)
<!-- 编写位置一:编写在html内部 -->
<a href="" onclick="alert('baidu')">百度一下</a>
- 编写在script元素之内
<script>
var baiduEl = document.querySelector(".baidu")
baiduEl.addEventListener("click",function(){
alert("hello world")
})
</script>
- 编写在独立的js文件中
<a href="" class="baidu">百度一下</a>
<script src="./index.js"></script>
<noscript>元素
- 如果运行的浏览器不支持JS,那么我们如何给用户更好的提示呢?
- 早期,需要一个页面优雅降级的处理方案
- 最终,<noscript>元素出现,被用于给不支持JS的浏览器提供替代内容
<body>
<script>
alert("hello world")
</script>
<noscript>
<p>你的浏览器不支持JS</p>
</noscript>
</body>
JS编写的注意事项
- script元素不能写成单标签
- 省略type属性
- 可以不写type=“text/javascript”,因为JS是所有现代浏览器以及HTML5中的默认脚本语言
- 加载顺序
- 作为HTMl文档的一部分,JS是遵循HTML文档的加载顺序,即自上而下的加载顺序
- 推荐将script内容放到body的子元素的最后一行
- JS代码严格区分大小写
- 而HTML元素和CSS属性不区分大小写
JS的交互方式
- JS有如下和用户交互的手段:
- alert
- console.log
- document.write
- prompt:接受用户的输入,有返回值
JS语句和分号
- 语句是向浏览器发出的指令,通常表达一个操作或者行为
- 比如我们前面编写的每一行代码都是一个语句,用于告知浏览器一条执行的命令
- 通常每条语句的后面我们会添加一个分号,表示语句的结束:
- 当存在换行符时,我们可以省略分号
- JS将换行符理解为隐式的分号
- 这样被称为自动插入分号
JS中的注释
- 单行注释://
- 多行注释:/**/
- 文档注释:/** 回车
JS的变量和数据类型
变量
- 如果我们希望记录某一个之后会变化的数据,在JS中我们可以定义一个变量
- 一个变量,就是一个用于存放数值的容器
- 可以是一个数字,字符串,或者其他任意数据
- 变量的独特之处在于它存放的数值是可以改变的
- 变量是一个容器,可以想象成一个盒子
变量的命名格式
- 在JS中命名一个变量包含两部分:
- 变量的声明:告诉JS引擎接下来我要定义一个变量
- 通过var关键字
- 变量赋值
- 通过等于号
- 变量的声明:告诉JS引擎接下来我要定义一个变量
变量的命名规范
- 首字符必须是字母、下划线、美元符号
- 不能使用关键字和保留字命名
- 关键字:有特殊含义的词
- 保留字:有一天可能成为关键字的词
- 变量严格区分大小写
以下是建议:
- 多个单词,建议使用小驼峰表示
- 赋值 = 两边加上空格
- 变量应该见名知意
变量练习题
<script>
// 定义两个变量保存两个数字,对两个变量中的数字进行交换
var num1 = 10
var num2 = 20
// 方式一:借助第三个变量
var temp = num1
num1 = num2
num2 = temp
// 方式二:不借助第三个变量
num1 = num1 + num2
num2 = num1 - num2
num1 = num1 - num2
</script>
变量的使用注意
- 如果一个变量未声明就直接使用,那么会直接报错
- 如果一个变量有声明,但是没有赋值,那么默认值是undefined
- 可以不使用var在全局声明一个变量(不推荐)
JS的数据类型
- JS中的值都具有特定的类型:
- 我们将值赋值给一个变量,那么这个变量就具备了特定的类型
- 一个变量前一刻是字符串,下一秒存储数字
- 这种被称为**“动态类型”**的编程语言
- JS中有8种基本的数据类型(7种原始类型和1种复杂类型)
- Number
- String
- Boolean
- Undefined
- Null
- Object
- BigInt
- Symbol
typeof操作符
- 因为ES的类型系统是松散的,所以需要一种手段来确定任意变量的数据类型
- typeof操作符为此而生
- typeof的返回值:
- undefined:值未定义
- boolean:布尔值
- string:字符串
- number:数值
- object:对象或null
- function:函数
- symbol:值为符号
- 操作符后面不需要跟小括号,后面可以加一个小括号,当作一个整体,并非是一个函数
Number类型
- number类型代表整数和浮点数
- 除了常规的数值,还包括特殊的数字
- Infinity:无穷大,也可以表示-Infinity
- 比如1/0就是无穷大
- NaN:不是一个数字,代表计算错误
- 比如字符串和数字相乘
- Infinity:无穷大,也可以表示-Infinity
NaN补充
- 等号运算符(和=)不能用来判断 一个值是否是NaN
- 必须使用Number.isNaN()或isNaN函数
- 在JS中,“NaN”与任何其他值(包括它自己)都不相等
String类型
- 来表示文本
- 转义字符:使用反斜杠\
- 字符串本身有的方法和属性
- split
- replace
- length
- 字符串操作
- 使用+号拼接
- 模版字符串
Boolean类型
- Boolean类型表示真假
Undefined类型
- 只有一个值,就是undefined
- 如果我们声明一个变量,但是没有对其进行初始化时,默认就是undefined
- 注意事项
- 最好在变量定义的时候进行初始化,而不只是声明一个变量
- 不要显式的将一个变量赋值为undefined
- 如果变量刚开始什么都没有,我们可以初始化0、空字符串、null等值
Object类型
- Object类型是一个特殊的类型,我们通常把它称为引用类型或者复杂类型
- 其他的数据类型我们通常称之为“原始类型”,因为它们的值包含一个单独的内容
- Object往往可以表示一组数据,是其他数据的一个集合
- 在JS中我们可以使用花括号{}来表示一个对象
Null类型
- Null类型同样只有一个值,即特殊值null
- null存在的意义就是对对象进行初始化的,并且在转成boolean类型时,会转成false
- typeof null 是object类型
<script>
// 以下转为boolean类型都是false
var age = 0
var num = 0
var message = ""
var isAdmin = false
// null类型
// 当对一个对象类型进行初始化时,不建议初始化为{},因为转为Boolean类型时,会认为是true
// 建议初始化为null
var book = {
}
if(book){
console.log("执行");
}
// null存在的意义就是对对象进行初始化的,并且在转成boolean类型时,会转成false
var books = null
</script>
- null和undefined的关系:
- undefined通常只有在一个变量声明但是未初始化时,它的默认值是undefined才会用到
- 我们不推荐直接给一个变量赋值为undefined,所以很少主动来使用
- null值非常有用,当一个变量准备保存一个对象,但是这个对象不确定时,我们可以先赋值为null
JS中的类型判断
三种方式,分别为typeof、instanceof和Object.prototype.toString.call()
typeof
- 通过typeof操作符来判断一个值属于哪种基本类型
- 在对象的子类型和null情况下,可以看出typeof对于判断类型还有一些不足
typeof 'seymoe' // 'string'
typeof true // 'boolean'
typeof 10 // 'number'
typeof Symbol() // 'symbol'
typeof null // 'object' 无法判定是否为 null
typeof undefined // 'undefined'
typeof {
} // 'object'
typeof [] // 'object'无法判定是否为array
typeof(() => {
}) // 'function'
instance of
- 通过instance of操作符也可以对对象类型进行判定
- 其原理就是测试构造函数的prototype是否出现在被测试对象的原型链上
[] instanceof Array // true
({
}) instanceof Object // true
(()=>{
}) instanceof Function // true
- 但是instance of仍然无法优雅的判断一个值到底属于数组还是普通对象
let arr = []
let obj = {
}
arr instanceof Array // true
arr instanceof Object // true
obj instanceof Object // true
obj instanceof Array // false
Object.prototype.toString()
Object.prototype.toString.call({
}) // '[object Object]'
Object.prototype.toString.call([]) // '[object Array]'
Object.prototype.toString.call(() => {
}) // '[object Function]'
Object.prototype.toString.call('seymoe') // '[object String]'
Object.prototype.toString.call(1) // '[object Number]'
Object.prototype.toString.call(true) // '[object Boolean]'
Object.prototype.toString.call(Symbol()) // '[object Symbol]'
Object.prototype.toString.call(null) // '[object Null]'
Object.prototype.toString.call(undefined) // '[object Undefined]'
Object.prototype.toString.call(new Date()) // '[object Date]'
Object.prototype.toString.call(Math) // '[object Math]'
Object.prototype.toString.call(new Set()) // '[object Set]'
Object.prototype.toString.call(new WeakSet()) // '[object WeakSet]'
Object.prototype.toString.call(new Map()) // '[object Map]'
Object.prototype.toString.call(new WeakMap()) // '[object WeakMap]'
Array.isArray()
- Array.isArray也可以判断传递参数是否是数组
- 是ES5.1推出的,不支持IE6~8
if (!Array.isArray) {
Array.isArray = function(arg) {
return Object.prototype.toString.call(arg) === '[object Array]';
};
}
空值判断
空值判断的重要性
if([]) {
console.log('true');
} else {
consoel.log('false');
}
// res:true
if({
}) {
console.log('true');
} else {
consoel.log('false');
}
// res:true
注意:空数组和空对象虽然为空,但是会被转换为true
空数组判断
- 首先通过类型判断是数组类型,然后它的length>0即可
空对象判断
-
Object.getOwnPropertyNames()
返回值是对象中属性名组成的数组
var obj = {
}
Object.getOwnpropertyNames(obj).length === 0
-
json对象转换为字符串
- json对象转换为字符串,然后比较该字符串与“{}”是否相等
var obj = { } var a = JSON.stringify(obj) a === "{}"
-
for…in…循环判断
var obj = { } function judgeObj(obj){ for(let key in obj){ return false } return true } judgeObj(obj)
-
Object.keys()
ES6的新方法,返回值同样是属性名组成的数组
var obj = { } Object.keys(obj).length === 0
-
使用对象的属性判断
如果obj不为空,一定会包含name属性
var obj = { } obj && obj.name ? "不为空" : "为空"
##数据类型之间的转换
- 在开发过程中,我们会经常对数据类型进行转换
- 大多数情况下,运算符和函数会自动将赋予它们的值转换为正确的类型,这是一种隐式转换
- 我们也可以通过显式的方式对数据类型进行转换
String类型的转换
- 方式一:隐式转换
- 一个字符串和其他类型进行+操作
- 如果+运算符左右两边有一个是字符串,那么另一边会自动转换为字符串类型进行拼接
- 某些函数的执行也会自动将参数转为字符串类型
- 比如console.log函数
- 一个字符串和其他类型进行+操作
- 方式二:显式转换
- 调用String()函数
- 调用toString()方法
数字类型Number的转换
- 转换方式一:隐式转换
- 除了加法的算术运算中,通常会将其他类型转换成数字类型来进行运算
- 比如"6"/“2”
- 除了加法的算术运算中,通常会将其他类型转换成数字类型来进行运算
- 转换方式二:显式转换
- 调用Number()函数
- 其他类型转换数字类型的规则
- undefined:NaN
- null:0
- true:1
- false:0
- string:去除首尾的空格后的纯数字,如果剩余字符串为空,则为0;如果有非数字元素,那么为NaN
Boolean类型的转换
- 方式一:隐式转换
- 发生在逻辑运算中
- 方式二:显示转换
- 调用Boolean()函数
无论是显式还是隐式,都有如下转换规则:
- 直观上为空的值(如0,空字符串,null,undefined,NaN)将变为false
- 其他值为true
JS基础运算符
算术运算符
- 用在数学表达式4中,使用方法和数学中也是一致的
- 算术运算符是对数据进行计算的符号
赋值运算符
- = 被称之为赋值运算符
- 链式赋值
- 从右往左进行计算
- 不推荐,相同的值,更推荐写三个
<script>
//链式赋值
// 将321赋值给前面所有的变量
var num1 = num2 = num3 = 321
console.log(num1,num2,num3);
</script>
原地修改
- 我们对一个变量做运算,并将新的结果存储在同一个变量中,就是原地修改
<script>
var num = 100
// 变量的原地修改
num = num + 10
num += 10
</script>
- 可以使用*运算符 += 和 = 来缩写这种表示
- 所有算术和位运算都有简短的"修改并赋值"运算符;/= 和 -= 等
自增和自减
- 对一个数加一、减一是非常常见的运算之一
- 所以有一些专门的运算符:
- 自增:++
- 自减:–
- 只能应用于变量
+++
- 运算符 ++ 和 – 可以置于变量前,也可以置于变量后
- 当运算符置于变量后,被称为”后置形式“
- 当运算符置于变量前,被称为”前置形式“
- 如果自增/自减的值不被使用,那么两者没有区别
- 如果被用于其他表达式计算中,就有区别
<script>
// 自增和自减表达式在其他的表达式中
var currentIndex = 5
// 105,currentIndex自增在表达式之后
var result = 100 + currentIndex++
// 106,currentIndex自增在表达式之前
var result = 100 + ++currentIndex
</script>
运算符的优先级
- 运算符放到一起使用会有一定的优先级
- 具体在MDN查看
比较运算符
- 大于/小于:a>b,a<b
- 大于等于/小于等于:a>=b,a<=b
- 检查两个值的相等:a == b
- 检查两个值不相等:!=
- 比较运算符得到的结果都是blooean类型的结果
和=的区别
<script>
var foo1 = undefined
var foo2 = null
// ==运算符,在类型不相同的情况下,通常会将运算元先转成Number类型的值,再进行比较
// null比较特殊,null在进行比较时,会被当成一个对象和原始类型比较,会返回false
// 但是null == undefined 的值是true,这是历史遗留问题
console.log(foo1 == foo2);
// ===运算符,在类型不相同的情况下,直接返回false
//null === undefined 的值是false
console.log(foo1 === foo2)
</script>
- 同样,!=和严格不相等!== 类似
- 双等号==
- 如果两个值类型相同,再进行三个等号的比较
- 如果类型不相同,需根据以下规则进行类型转换
- 如果一个是null,一个是undefined,那么相等
- 如果一个是字符串,一个是数值,把字符串转换为数值之后再进行比较
- 三等号===
- 如果类型不同,就一定不相等
- 如果两个都是数值,并且是同一个值,那么相等;如果其中至少一个是NaN,那么不相等。(判断一个值是否是NaN,只能使用isNaN( ) 来判断)
- 如果两个都是字符串,每个位置的字符都一样,那么相等,否则不相等。
- 如果两个值都是true,或是false,那么相等
- 如果两个值都引用同一个对象或是函数,那么相等,否则不相等
- 如果两个值都是null,或是undefined,那么相等
JS分支语句和逻辑运算符
单分支结构
-
单分支语句:if
if(条件判断){ //执行代码块 }
-
如果代码块中只有一行代码,可以省略大括号{}
-
if(…)会计算圆括号内的表达式,并将计算结果转换为布尔值
- 转换规则和Boolean函数的规则一致
- 数字0、空字符串、null、undefined和NaN都会被转换为false
- 因此它们被称为“假值”
- 其他值被转换为true,被称为"真值"
多分支语句
- 多分支语句:if…else…
if(条件){
}else{
}
- 多分支结构:if…else if… else…
- 判断多个条件时使用
三元运算符
- 根据一个条件去赋值变量
- 使用if…else不够简洁
- 条件运算符:?
- 也称为"三元运算符"
- 使用格式
var result = condition ? value1 : value2;
- 给变量赋值一个默认值
var info = {
name:"why",
age:18
}
//如果info有值则赋值给obj,否则赋值一个空对象
var obj = info ? info : {
}
逻辑运算符
- 逻辑运算符,主要有三个:
- 或||,与&&,非!
- 它可以将多个表达式或者值放到一起来获取到一个最终的值
逻辑运算符||的本质
- 逻辑或也被称为短路或
- 本质:
- 从左往右依次计算运算元
- 先将运算元转成Boolean类型,对Boolean类型进行判断
- 如果为true则直接将结果(元类型,初始值)返回
- 如果为false,进行第二个运算元的判断
- 依次类推
- 如果没有找到true,则返回最后一个运算元
- 本质推导:获取第一个有值的结果
var info = undefined
//防止info没值时的报错
var message = info || "默认值"
console.log(message.length)
逻辑与&&的本质
- 本质:
- 那个第一个运算元,转换成Boolean类型进行判断
- 如果为true,查找下一个继续进行运算
- 如果为false,返回运算元(原始值)
- 依次类推
- 如果所有的都为true,那么返回最后一个运算元
- 那个第一个运算元,转换成Boolean类型进行判断
- 本质推导一:逻辑与,也可以称之为短路与
- 本质推导二:对一些对象中的方法进行有值判断
var obj = {
name:"why",
friend:{
name:"kobe",
eating:function(){
console.log("eating something");
}
}
}
// 调用eating函数
// obj.friend.eating()
// 如果obj和obj.friend都有值的情况下,再回执行eating函数
obj && obj.friend && obj.friend.eating && obj.friend.eating()
逻辑非!的补充使用
var message = "hello world"
// 将message转换为Boolean类型
console.log(Boolean(message));
console.log(!!message