JavaScript入门:基本语法
JavaScript入门
引入
什么是javaScript?
JavaScript是什么?
- JavaScript是一门语言,其实它既可以做前端也可以做后端
javascript可以用来做什么?
- 大多数时间在浏览器里做交互式网页
- 也可以做移动端app、实时联网app、命令行工具 、游戏
JavaScript运行在哪里
- 最开始被设计成只能运行在浏览器里、所以每个浏览器都有一个JavaScript引擎(火狐SpiderMonkey;Chrome V8)
- 2009年 Ryan Dahl 将 Chrome V8 开源部分与C++结合 做了Node
- 所以现在 浏览器或者 Node 都可以作为 运行时环境
简单交互
输出
1、控制台交互,可以直接在控制台写,也可以在script标签写
console.log()
2、弹窗交互
alert("hello")
3、body内输出
document.write("hello")
输入
. 输入语法
prompt("XXX")
显示一个对话框,对话框中包含一条文字信息,用来提示用户输入文字
let username = prompt("请输入您的姓名:")
注意:
JavaScript 代码执行顺序:
按HTML文档流顺序执行JavaScript代码
alert() 和 prompt() 它们会跳过页面渲染先被执行
坑: 使用表单、prompt 获取过来的数据默认是字符串类型的
JavaScript 组成
JavaScript的语法规范:有很多种,这里学习最常用的 JavaScript ECMAScript
规定了js基础语法核心知识。
比如:变量、分支语句、循环语句、对象等等
Web APIs:
- DOM 操作文档,比如对页面元素进行移动、大小、添加删除等操作
- BOM 操作浏览器,比如页面弹窗,检测窗口宽度、存储数据到浏览器等等
权威网站: MDN
搭建JavaScript开发环境
1、下载VSCode 作为 编辑工具
2、下载 Node 作为运行工具
当我们仙剑一个html文件,只输入一个!号,VSCode就会帮我们自动补全代码。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>Hello World</h1>
</body>
</html>
JavaScript 程序不能独立运行,它需要被嵌入 HTML 中,然后浏览器才能执行 JavaScript 代码。通过 script
标签将 JavaScript 代码引入到 HTML 中.
在vscode中安装 live server
这样右键点击文件,就会从浏览器中加载我们的html文件了
我们可以在 html文件里面的 head标签或者 body标签里面添加 JavaScript脚本(
<script>
标签)一般一个好的编程习惯是,将JavaScript 标签放在 body标签的末尾,
- 1、浏览器解析这个文件是自顶向下的顺序执行的,如果放在head中,如果有很多JavaScript代码,浏览器就会忙于解析JavaScript代码,而不能即使加载页面,用户体验差
- 2、jsp代码一般需要与许多控件交互,如果需要等许多控件渲染完成才能解析完成,
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>Hello World</h1>
<script>
// 在控制台打印 hello hello
console.log("hello hello")
</script>
</body>
</html>
其实,我们一般非常希望将JavaScript脚本从页面“剥离出来”
因为 因为 html 页面更多“关注” 静态页面显示的内容,而 JavaScript 更多“关注” 与页面交互时的行为
我们添加一个 相同目录添加一个文件 “XX.js” 将 <script>
标签里面
的内容剪切粘贴
在html文件里面引用这个js文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>Hello World</h1>
+ <script src="index.js"> </script>
</body>
</html>
使用Node 运行 JavaScript 代码
在 VScode 里面 用快捷键 ctrl
+~
从vscode中打开终端
JavaScript数据类型
变量与常量
在JavaScript中组要通过三个关键字声明变量和常量,分别是 var、let 和 const
var 在JavaScript诞生的时候就有了,但是现在已经很少使用了
var是全局作用域的,在一个if条件作用域的内外声明两个同名的变量会导致冲突
Ø 可以先使用 在声明 (不合理)
Ø var 声明过的变量可以重复声明(不合理)
Ø 比如变量提升、全局变量、没有块级作用域等等
var 就是个bug,别迷恋它了,以后声明变量我们统一使用 let
let 和 const 都是 ES6 中新加入的 关键字,他们只在时作用域是局部的
- let 修饰的值可以被修改
- const 修饰的值不能被修改,但是如果const修饰的变量是数组或者对象时,其内部的元素是可以被改变的
- const 必须在声明时就初始化数值,否组会报错
JavaScript原生数据类型
主要有 String、Number(js不区分整数和小数)、Boolean、null、undefined
String
- 用单引号或者双引号修饰
const username = 'name'
Number
- Js定义时不区分小数或者整数
let score=98
const pi = 3.14
Boolean
let flag = true
null
let x=null
undefined
const y= undefined
let z
不指定类型默认 undefinednull 和 undefined 区别:
undefined 表示没有赋值
null 表示赋值了,但是内容为空,将来有个变量里面存放的是一个对象,但是对象还没创建好,可以先给个null
用 console.log(typeof XXX)
查看变量的数据类型
但是当你用 console.log(typeof x)
(上面x被定义为nul类型),打印出来却是object
这是一个历史遗留bug,,,早期设计编程语言时用低位存储变量类型信息,如
- 000:对象
- 1:整数
- 010:浮点数
- 100:字符串
- 110:布尔
但有两个类型比较特殊
undefined
:用 -2^{30} (−2^30)表示。null
:对应机器码的NULL
指针,一般是全零。
有人层提出修复这个bug,但被否决了,现在成为了这门编程语言的feature 😀
字符串
字符串的拼接:
1、加号拼接
2、模板字符串
const username = “Bob”;
const s1=”My name is ${username}”
console.log(s1)
字符串的内置方法
转大写/小写
str.toUpperCase()
str.toLowerCase()
截取
str.substring(0,5) //截取 字符串 索引 0到4
分割
str.split(‘’)// 返回被分割的字符串数组,当输入为空字符串时,为最小分割
类型转换
隐式转换
+ 号两边只要有一个是字符串,都会把另外一个转成字符串;+号作为正号解析可以转换成数字型
除了+以外的算术运算符 比如 - * / 等都会把数据转成数字类型
显式转换
转换为数字型
1、Number(数据)
转成数字类型
如果字符串内容里有非数字,转换失败时结果为 NaN(Not a Number)即不是一个数字
NaN也是number类型的数据,代表非数字
2、 parseInt(数据)
只保留整数
3、 parseFloat(数据)
可以保留小数
转换为字符形
1、String(数据)
2、变量.toString(进制)
数组
创建数组
构造函数(用的少)
const numbers = new Array(1,2,3,4);
直接中括号
const hh = ["apples","aranges","pears",10,true]
- 注意JavaScript数组可以时不同的数据类型
数组增删查改
这里用const申明数组hh可以认为hh的指针不变,只要指针不变,其他关于数组的改变都可以被允许,包括改变数组某个位置的元素,或者新增元素改变了数组的大小
hh.push("hh_tail")
末尾加元素,hh.unshift("hh_head")
头部加元素hh.pop();
末尾删去元素,hh.shift()
头部删去元素hh.splice(start,deleteCount), 方法 删除指定元素,以及删除几个元素,deleteCount省略的化默认删到最后
其他
Array.isArray(hh)
hh.indexOf(0)
数组遍历
1、for(let i =0;i<numbers.length();i++)
2、map方法遍历
map 可以处理数据,并且返回新的数组
const arr = ['pink','red','blue']
const newArr = arr.map(function (item,index){
console.log(item)
console.log(index)
return item + '老师'
})
console.log(newArr)
join方法
join() 方法用于把数组中的所有元素转换一个字符串
const arr = ['pink','red','blue']
console.log(arr.join(arr,'')) // 'pinkredblue'
数组元素是通过参数里面指定的分隔符进行分隔的
map+join常用于渲染
渲染模块
(3) 我们使用map方法遍历数组,直接返回 整个tr, 里面包含所有修改后的 tr 标签, 里面更换数据
(4) 但是map方法返回的是一个修改后的数组,怎么办?
我们通过join方法转换为字符串
(5) 把返回的结果, 通过 innerHTML 赋值给 tbody
对象
声明对象,通过大括号声明对象,对象里以键值对(属性 冒号 值)的形式表示属性,属性的值可以是任何类型的变量
const person ={
firstName : "John",
lastName : "Deo",
age : 30,
hobbies:['music','moive'],
address:{
country:"china",
city:"beijing",
street:"xitucheng"
}
}
// 通过new Object 创建
const o = new Object({name:"佩奇"})
可以通过点+属性名的方式获取任何一个属性 ,或者对象[‘属性名’]
如
person.firstname
person['firstname']
// 第二种方式功能更强大 适用于非正常属性,但必须加引号,否则退化成第一种
可以从或如下方式从对象中“抽取”对应变量名的变量值
const {firstName,lastName}=person;
const{firstName,address:{city}} =person;
console.log(city);
js里面的对象感觉并不像真正的对象,他随时可以添加/删除或者更新属性
如添加/更改属性
person.email='122@gmail.com'
删除对象属性
delete person.email
对象数组和json
const todos = [
{
id:1
text: "清理垃圾"
}
{
id:2
text: "写代码"
}
{
id:3
text: "找工作"
}
]
console.log(todos[1].text)
json是一种数据格式
json有两种格式
- 对象格式
- 对象数组格式
严格来说,数组对象以及对象与json还是有去别的:
- json 属性名(键)必须加
双
引号!
对于JavaScript来说怎么将数组对象/数组转换为json格式(字符串)呢?
const todosJson = JSON.stringify(todos);
console.log(todoJson)
构造函数
function Pig(name,age){
this.name = name
this.age=age
}
// 创建对象
const Peiqi = new Pig("佩奇",10)
const date = new Date('2022-4-8')
构造函数在技术上是常规函数。
不过有两个约定:
- 它们的命名以大写字母开头。
- 它们只能由 “new” 操作符来执行。
说明:
- 使用 new 关键字调用函数的行为被称为实例化
- 实例化构造函数时没有参数时可以省略 ()
- 构造函数内部无需写return,返回值即为新创建的对象
- 构造函数内部的 return 返回的值无效,所以不要写return
- new Object() new Date() 也是实例化构造函数
实例成员和静态成员
function Pig(name,age){
// 实例成员 实例函数 定义部分
this.name = name
this.age=age
this.sayHello = function{
console.log("hello")
console.log(this) //输出 object
}
}
// 静态成员 静态函数定义部分
Pig.numsOfLeg=4
Pig.say=function(){
console.log("嗝~")
console.log(this) //输出 Pig(name,age){...}
}
静态成员方法中的 this(如果有) 指向构造函数本身
prototype
对于js来说。每一个实例方法,每一个实例都会创建一个实例方法,按理说实例方法其实应该是公用的,如果每一个实例都创建会极大的浪费内存
用什么来解决呢?
JavaScript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象,所以我们也称为原型对象
这个对象可以挂载函数,对象实例化不会多次创建原型上函数,节约内存
我们可以把那些不变的方法,直接定义在 prototype 对象上,这样所有对象的实例就可以共享这些方法。 构造函数和原型对象中的this 都指向 实例化的对象
function Star(uname,age){
this.name= uname;
this.age = age
}
Star.prototype.sing=fucntion(){
console.log("唱")
}
const ldh = new Star('刘德华', 18)
const zxy = new Star('张学友', 19)
console.log(ldh.sing === zxy.sing) // true ,如果直接在构造函数里面的是 false
constructor(属于 prototype)
function Star(name){
this.name = name
}
Star.prototype = {
sing: function() {}
dance: function() {}
}
console.log(Star.prototype.constructor) //指向Object
function Star(name){
this.name = name
}
Star.prototype = {
constructor: Star
sing: function() {}
dance: function() {}
}
console.log(Star.prototype.constructor) //指向 Star
__proto__
对象都会有一个属性 __proto__
指向构造函数的 prototype 原型对象,之所以我们对象可以使用构造函数 prototype
原型对象的属性和方法,就是因为对象有 __proto__
原型的存在
<img src="https://differencer.oss-cn-beijing.aliyuncs.com/img/image-20230812112647114.png" alt="image-20230812112647114" style="zoom:67%;" />
ldh.__prototype__ === Star.prototype // true
ldh.__prototype__.constructor == Star
简单记忆:
- 构造函数 Star相当于 父亲
prototype
于__proto__
相当于 兄弟之间的关系?
继承
原型继承
const People = {
head : 1,
eyes : 2,
legs : 2,
say: function(){}
}
function Man(){}
Man.prototype = People
Man.prototype.constructor = Man
const man1 = new Man()
问题:如果我们给男人添加了一个吸烟的方法,发现女人自动也添加这个方法
原因:男人和女人都同时使用了同一个对象,根据引用类型的特点,他们指向同一个对象,修改一个就会都影响
需求:男人和女人不要使用同一个对象,但是不同对象里面包含相同的属性和方法
答案:构造函数
上面改为
// 构造函数
function Man(){}
Man.prototype = new People()
Man.prototype.constructor = Man
Man.prototype.smoke = function(){ console.log("smoke")}
原型链
原型链-查找规则
① 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。
② 如果没有就查找它的原型(也就是 proto指向的 prototype 原型对象)
③ 如果还没有就查找原型对象的原型(Object的原型对象)
④ 依此类推一直找到 Object 为止(null)
⑤ proto对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线
⑥ 可以使用 instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上
如 Array的 的map 方法,
他其实是 不是 Array构造函数里面的方法,他是 protoype里面的方法
基本数据类型/引用数据类型
简单类型又叫做基本数据类型或者值类型,复杂类型又叫做引用类型
值类型:简单数据类型/基本数据类型,在存储时变量中存储的是值本身,因此叫做值类型
引用类型:复杂数据类型,在存储时变量中存储的仅仅是地址(引用),因此叫做引用数据类型通过 new 关键字创建的对象(系统对象、自定义对象),如 Object、Array、Date等
堆栈空间分配区别:
1、栈(操作系统):由操作系统自动分配释放存放函数的参数值、局部变量的值等。其操作方式类似于数据结构中的栈;
值类型变量的数据直接存放在变量(栈空间)中
2、堆(操作系统):存储复杂类型(对象),一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收。
引用类型变量(栈空间)里存放的是地址,真正的对象实例存放在堆空间中
流程控制
if条件语句/ == 和 ===
const x = 10
const y = "10"
const o1={
num1 : x,
}
const o2 ={
num1 : y,
}
if(x==y){
console.log("10==\"10\"")
}
if(x===y){
console.log("10===\"10\"")
}
if(o1==o2){
console.log("o1==o2")
}
o2.num1=x;
if(o1==o2){
console.log("o1==o2")
}
if(o1===o2){
console.log("o1===\"o2\"")
}
上述语句只会打印 10=="10"
switch语句 (略)
for while
for(let i=0;i<10;i++){
console.log(i)
}
let j=0
while(j<10){
console.log(j)
j++;
}
逻辑中断
function getSun(x,y){
x = x || 0
y = y || 0
console.log(x+y)
}
getSum(1,2) // 输出3
getSum() // 输出 0
// 起作用类似于 参数默认值
原理,x = x || 0 这一句,x 输入 非0 非 undefined 非 null 等值全作为真来看待,就不执行后面了,如果输入不满足前面的就认为是真,真则不看后面的值了
PS:
- ‘’ 、0、undefined、null、false、NaN 转换为布尔值后都是false, 其余则为 true
- 有字符串的加法 “” + 1 ,结果是 “1”
- 减法 - (像大多数数学运算一样)只能用于数字,它会使空字符串 “” 转换为 0
- null 经过数字转换之后会变为 0
- undefined 经过数字转换之后会变为 NaN
运算符 && 与此类似 (&& 、 || 也叫短路与、 短路 或)
函数
普通函数
// 声明
function 函数名(参数列表){
函数体
(return 返回值)
}
// 调用
let x = 函数名(参数值列表)
如果参数列表不给值,会怎么样?默认传undefined,如果对undefined进行运算会得到NaN的结果
为了避免不传参报错的情况,可以再定义函数参数时设置默认值(每个参数加等号和值)
匿名函数
匿名函数:将匿名函数赋值给一个变量,并且通过变量名称进行调用 我们将这个称为函数表达式
// 声明时将函数值给一个变量
let yy = function(参数列表){
}
// 调用
let y =yy(参数值列表);
立即执行函数
场景介绍: 避免全局变量之间的污染
//方式1
(function (){ console.log(11) })();
//方式2
(function(){console.log(11)}());
// 注意: 多个立即执行函数要用 ; 隔开,要不然会报错
对象
对象函数
之前介绍对象时,只介绍了它通过键值对的方式存储属性,其实它还可以通过键值对(方法名:函数)的方式存储行为
遍历对象(属性)
let obj = {
uname : 'andy',
age : 18,
sex : '男'
}
// 遍历
for(let k in obj){
console.log(k) // 打印属性名
console.log(obj[k]) // 打印属性值
}
注意通过增强for 循环获取时,变量为对象的属性名,所以通过中括号获取属性值
内置对象
我们使用的console 以及 document 其实就是内置对象
内置对象-Math
Math.random()
- 随机数函数, 返回一个0 - 1之间,并且包括0不包括1的随机小数 [0, 1)
- 如何生成5-12的随机数?
Math.floor(Math.random()*(7+1))+5
Math.ceil()
向上取整Math.floor()
向下取整Math.max()
Math.min()
Math.pow()
Math.abs()