var关键字
要定义变量,可以使用var操作符(注意var是一个关键字),后跟变量名,用他也可以保存任何类型的值。
var name
var name="likeqin"
上面 name被定义为一个保存字符串值的likeqin的变量,只是一个简单的赋值,可以改变值也可以改变值的类型,如下:
var name="likeqin"
likeqin=100;//合法但不推荐
1.var 声明作用域
使用var操作符定义的变量会成为包含它的函数的局部变量,比如使用var在一个函数内部定义一个变量,那么该变量在函数退出时被销毁。例如:
function test(){
var name="hi";//局部变量
}
test();
console.log(name);//报错!!!!
上面这个name变量是在函数内部使用var定义的 函数叫test(),调用它 会创建这个变量并给它赋值。调用之后随之被销毁 所以报错!
如果需要定义多个变量,用逗号分隔每一个变量
var name="hi",
lalal=kkk,
age=123;
2.var声明提升
console.log(x)//undefined
var x=1;
使用var时,上面的代码不会报错,因为使用这个关键字声明的变量会自动提升到函数作用域顶部。
var反复多次声明同一个变量也没有问题。
var x=1;
var x=12;
var x=1221;
console.log(x)//1221
let声明
let跟var的作用差不多,但是有着非常重要的区别。最明显的区别是,let声明的范围是块作用域,而var声明的范围是函数作用域。
if(true){
var name='Matt';
console.log(name);//Matt
}
console.log(name);//Matt
if(true){
let age=22;
console.log(age);//22
}
console.log(age);//ReferenceError:age没有定义
let不允许同一个块作用域中出现冗余声明,会报错
let age;
let age; //SyntaxError:标识符age已经声明过了
当然,JavaScript引擎会记录用于变量声明的标识符及其所在的块作用域,因此嵌套使用相同的标识符不会报错,而这是因为同一个块中没有重复声明。
var name='shenmowanyi';
console.log(name); //'shenmowanyi'
if(true){
var name='hahaha';
console.log(name); //'hahaha'
}
let age=50;
console.log(age); //50
if(true){
let age=60;
console.log(age); //60
}
对声明冗余报错不会因为混用let和var受影响。这两个关键字声明的并不是不同类型的变量,他们只是指出变量在相关作用域如何存在。
var name;
let name; //SyntaxError
1.暂时性死区
let与var另一个最重要的区别就是,let声明的变量不会在作用域中被提升。
不举代码例子了大家应该都知道。
在解析代码时,JavaScript引擎也会注意出现在块后面的let声明,只不过在此之前不能以任何方式来引用未声明的变量。在let声明之前的执行瞬间被称为“暂时性死区”(temporal dead zone),在此阶段引用任何后面才声明的变量都会抛出Reference Error
2.全局声明
与var关键字不同,使用let在全局作用域中声明变量不会成为window对象的属性 var会
var x=20
console.log(window.x) //20
let y=30
console.log(window.y) //undefined
3.条件声明
let的作用域是块,所以不可能检查前面是否已经使用let声明过同名变量,同时也就不能再没有声明的情况下声明它。
不能进行let条件声明是件好事,它是一种反模式,让程序变得更难理解。
4.for循环中的let声明
在let出现之前,for循环定义的迭代变量会渗透到循环体外部:
for(var i=0; i<5; i++){
}
console.log(i)//5
改成let之后,这个问题就消失了,因为迭代变量的作用域仅限于for循环快内部:
for(let i=0; i<5; i++){
}
console.log(i)//报错 i没有被定义
在使用var的时候,最常见的问题就是对迭代变量的奇特声明和修改:
for(var i=0; i<5; i++){
setTimeout(()=>console.log(i),0)
}
//你可能以为会输出0、1、2、3、4
//其实会输出 5、5、5、5、5
之所以会这样,是因为在退出循环时,迭代变量保存的是导致循环退出的值:5 。在之后执行超时逻辑时,所有的i都是用一个变量所以输出同一个值。
而在使用let声明迭代变量时js引擎会在后台为每个迭代循环声明一个新的迭代变量。每个setTimeout引用的都是不同的变量实例,所以输出的是我们期望的值,也就是循环执行过程中每个迭代变量的值。
for(let i=0; i<5; i++){
setTimeout(()=>console.log(i),0)
} //输出0、1、2、3、4
const声明
let const let定义变量 const定义常量
let x=20
x=40
console.log(x)
const http="http://localhost:8088"
http=123
console.log(http)
声明风格及最佳实践
1.不使用var
有了let和const,大多数开发者会发现自己不再需要var了。限制自己只使用let和const有助于提升代码质量,因为变量有了明确的作用域、声明位置,以及不变的值。
2.const优先,let次之
使用const声明可以让浏览器运行时强制保持变量不变,也可以让静态代码分析工具提前发现不合法的操作。因此,很多开发者认为应该优先使用const来声明变量,只在提前知道未来会有修改时,再使用let。这样可以让开发者更有信心的推断某些变量的值永远不会变,同时也能迅速发现因为意外赋值导致的非预期行为。
二、字符串模板
//字符串模块 快速字符串和变量拼接
let area=100//
//面积为 area 平方米
console.log("面积为"+area+"平方米")
//使用字符串模板进行
//`` 里面的变量放到${变量名}
console.log(`面积为"${area}"平方米`)
三、数据结构(先简单了解)
解构:顾名思义 将复杂结构中内容,解构成简单结构
let user={
name:"wagnyi",
age:21,
uimg:"123123.png"
}
需求中需要大量的使用 对象中的某一个属性
user.name
让name变量对应 user.name属性
name变量 解构
解构的语法:左右结构相同
左侧和右侧的key相对应
let {name,age}=user
console.log(name)//小王
console.log(age)//21
let arr=[1,23,4]
let [a,b,c]=arr
console.log(a)//1
console.log(b)//23
console.log(c)//4
解构做交互变量
let a=20
let b=30;
[a,b]=[b,a]
var x=30
console.log(++x)
console.log(a)//30
console.log(b)//20
四、箭头函数
1.传统的function定义函数 this指向性不明确 , 随着使用环境的变化发生改变
function show(){
console.log(this)
}//this指向谁??
show()//window 直接调用show就指向window
或者:
<button id="bt1">点击我</button>
<script>
function show(){
console.log(this)
}
bt1.onclick=show // bt1 变成事件源了
</script>
再或者:
function show(){
console.log(this)
}
//封装到对象的方法里
let obj={
name:"wangyi",
show:show
}
obj.show()//obj
箭头函数: this指向明确 ,永远指向生产环境
定义一个箭头函数:
<button id="bt1">确定</button>
//如何定义一个箭头函数
let show=()=>{
console.log(this)
}
show()//window
bt1.onclick=show //还是window
//let obj={
// name:"wangyi",
//show:show
//}
// obj.show()//还是window
2.传统function定义 有默认的提升功能,箭头函数没有。因为箭头函数是用let语句定义的因为let语句没有提升功能 所以箭头函数也没有 如果这么做会报错。
3.传统function 可以作为构造函数,箭头函数不可以
因为this的指向性 箭头函数永远指向生产环境
4.传统function 函数中具有arguments参数集合,箭头函数没有 只有 rest 参数,rest定义才会有
什么是arguments参数集合(用实参决定内容 有点反逻辑) :
举个例子:编写一个函数 计算n个数字的和 ,用户传入多少,就计算多少
function sum(){
//每个函数内部都有一个arguments集合
console.log(arguments) //Arguments(10) [1, 2, 3, 4, 5, 6, 7, 9, 10, 12, callee: ƒ, Symbol(Symbol.iterator): ƒ] 实参的集合
let s=0
for(let n=0;n<arguments.length;n++){
s+=n
}
return s
}
console.log(sum(1,2,3,4,5,6,7,9,10,12))//45
如何定义rest参数:用了一个拓展运算符的语法 rest参数
let show=(...args)=>{ //把所有的实参都放到args(args叫什么名字都可以)里
console.log(args)
}
show(1,2,3,4) //输出数组 [1, 2, 3, 4]
arguments参数集合与rest 参数的区别在哪 一个是集合一个是数组
但重点是:arguments与形参没有任何关系 永远是实参的集合 就像下面的代码一样
function sum(a,b,c){
console.log(arguments) // 还是输出 Arguments(10) [1, 2, 3, 4, 5, 6, 7, 9, 10, 12, callee: ƒ, Symbol(Symbol.iterator): ƒ] 实参的集合
}
console.log(sum(1,2,3,4,5,6,7,9,10,12))//45
但是rest前面可以加形参 rest只能加在形参的后面 然后会抛出形参外所有实参 ,例如下面这串代码会输出什么:
let show=(a,b,...args)=>{
console.log(args)
}
show(1,2,3,4) //输出数组 [3, 4]
箭头函数应用场景---定时变色
点击后3s 背景色变成蓝色
<style>
.box{
width: 300px;
height: 300px;
background-color: red;
}
</style>
<div class="box">
</div>
<script>
let box=document.getElementsByClassName("box")[0]
box.onclick=function(){
//延时定时器:异步操作 统一在window进行 所以定时器的this指向性指向window
//所以会报错
//setTimeout(function(){ },3000) 延时定时器
setTimeout(function(){
//console.log(this)
that.style.backgroundColor="blue"
},3000)
}
</script>
正常思路会用 延时定时器:异步操作 统一在window进行 所以定时器的this指向性指向window
然后会报错
如何解决(第一种原本简单的解决方式):
let box=document.getElementsByClassName("box")[0]
box.onclick=function(){
let that=this
setTimeout(function(){
//console.log(that)
that.style.backgroundColor="blue"
},3000)
}
用let变量存一个this let that=this 然后就会用that来表示
还可以用箭头函数来解决:因为箭头函数this永远指向与它所创建的环境,下面的代码箭头函数是在事件处理函数里面创建的所以 永远指向事件源
let box=document.getElementsByClassName("box")[0]
box.onclick=function(){
setTimeout(()=>{
this.style.backgroundColor="blue"
},3000)
}
在ES6里 只是增加了一个小功能 并不是箭头函数替代了function
function:在构造函数的时候或者很多时候都可以用
箭头函数:this指向性不变的 使用箭头函数
箭头函数简写:
编写一个方法,传入一个参数,返回其平方
1.如果箭头函数中有且只有一个参数,那么可以省略括号()
let pow=(n)=>{return n*n}
let pow=n=>{return n*n} //省略后
2.如果箭头函数内部有且只有一行语句,并且是return语句的话 可以省略{}和return
let pow=n=>{return n*n}
let pow= n=>n*n //省略后