//两种生成全局变量的方法(都是写在函数体外)
var a=123;
b=456;
//两种方法生成全局变量的区别:
//1、
//没有使用 var 生成的全局变量,是作为window的属性存在,
//不是真正意义上的全局变量,可以用 delete b (或者delete window.b)删除,删除之后获取b则是 not defind,
//而通过 var 生成的全局变量,无法使用 delete 删除
//2、
//在函数内部,没有使用 var 创建的变量,是全局变量;反之则是局部作用域
作用域包括:全局作用域(global,window),函数作用域(function,局部作用域),块级作用域({}),动态作用域(this)
注:ES5及之前,作用域只有全局作用域和局部作用域(函数作用域),没有块作用域的概念,
function test(){
var a=1;
if(a == 1){
var b=2;
console.log(a)
}else{
console.log(a)
}
console.log(b) //ES5环境中输出 2 ,因为ES5没有块级作用域
}
var,let,const 区别
1)var,let 是定义变量(定义后可以修改),const 是定义常量(定义后无法修改,声明时就需要赋值,不能先声明后赋值)
2)var 声明的变量具有全局作用域或者函数作用域,let 和 const 声明的变量/常量具有块级作用域
3)var 声明的全局变量可以使用全局对象的属性去访问,let,const 声明的全局变量不能用全局对象的属性(即 window.a)获取,
4)var 可以重复声明定义同一个变量,但是 let,const 不可以重复用定义同一个变量
5)var 声明的变量有变量提升,let,const 没有变量提升
例:
let likearry={
0:'a',
1:'b',
length:2
}
//ES5
let args=[].slice.call(arguments) //collection 集合转换为数组
let imgs-[].slice.call(document.querySelectorAll('img')) //NodeList node列表转换为数组
//ES6 (Array.from)
let args=Array.from(arguments)
let imgs=Array.from(document.querySelectorAll('img'))
Array.from(arrayLike,mapFn,thisArg)
arrayLike:伪数组,mapFn:遍历函数,thisArg:this
将一组数值转化为数组
Array.of() //[]
Array.of(3,11,18) //[3,11,18]
Array.of(3) //[3]
Array.of(3).length //1
Array.of 方法可以用以下代码模拟实现
function ArrayOf(){
return [].slice.call(arguments)
}
注:Array.of 总是返回参数值组成的数组,如果没有参数就返回一个空数组
填充数组
说明:该方法接收三个参数,
若没有参数array.fill()
,则数组每一项都为 undefind
若有一个参数array.fill(1)
,则将数组的每一项都修改为1
若有两个参数array.fill(1,2)
,则将数组中下标>=2的对应的数组值改为1
若有三个参数array.fill(1,2,4)
,则将数组中下标>=2,<4的值改为1
let array=Array(5).fill(7); //[7,7,7,7,7]
['a','b','c'].fill(7); //[7,7,7]
new Array(3).fill(7); //[7,7,7]
['a','b','c','d'].fill(7,1,3); //['a','7','7','d']
['a','b','c','d'].fill(); //[undefined, undefined, undefined, undefined]
['a','b','c','d'].fill(7,1); //["a", 7, 7, 7]
查找数组中第一个符合条件的数组成员,参数是一个回调函数。返回符合条件的数组成员,如果没有符合条件的成员,返回 undefind
let arr=[1,2,3,4,5];
let val=arr.find(function(item,index,arr){ //参数分别是当前的数组元素,当前索引值,被查找的数组
return intem % 2 === 0
})
console.log(val); //2
ES5中的
filter
也可以用来进行数组查找let arr=[1,2,3,4,5]; let result=arr.filter(function(oitem){ return item %2 ==0 }) console.log(result) //[2,4]
filter
与find
的区别:
filter
即使查到符合条件的成员,仍然会继续往下查找,不会停止,而find
只要查到符合条件的成员就是停止查找,返回第一个符合条件的成员
查找符合条件的数组成员的位置,若所以成员都不符合,则返回 -1
let arr=[1,2,3,4,5,6];
let result = arr.findIndex(function(item ){
return item > 5;
})
console.log(result ) //5
1)ES5 中定义类
法一:
let Animal=function(){
this.type=type;
this.walk=function(){
console.log('I am walking');
}
}
let dog=new Animal('dog');
let monkey=new Animal('monkey');
monkey.walk=function (){
console.log('monkey is walking')
}
monkey.walk(); //monkey is walking
dog.walk(); //I am walking
该写法将每个方法挂载到了实例对象上面,导致每个实例对象会大
法二:
let Animal=function(){
this.type=type;
}
Animal.prototype.walk=function(){
console.log('I am walking');
}
let dog=new Animal('dog');
let monkey=new Animal('monkey');
monkey.walk=function(){
console.log('change type')
};
console.log(dog);
console.log(monkey);
该写法将
walk
方法放到了原型链(原型链上做继承)上,实例对象不会很大,dog
和monkey
实例中的,若修改Animal.prototype.walk
中的逻辑,所有实例对象都会受影响,但是若修改实例对象上的walk
,,其他对象上的walk
方法不会被修改。
延申:
let Animal=function(){
this.type=type;
}
Animal.prototype.walk=function(){
console.log('I am walking');
}
let dog=new Animal('dog');
let monkey=![在这里插入图片描述](https://img-blog.csdnimg.cn/20200320155730523.png)new Animal('monkey');
monkey.constructor.prototype.walk=function(){
console.log('change type')
};
console.log(dog);
console.log(monkey);
monkey.constructor.prototype.walk
这种写法修改walk
,会修改共同的walk
方法
2)ES6 创建 class 类
class Animal{
constructor(type){ //构造函数,里面写属性
this.type=type;
}
walk(){ //方法
console.log('I am walking');
}
}
let dog=new Animal('dog');
let monkey=new Animal('monkey');
虽然 ES6 在类的定义是仅是 ES5 定义类的语法糖,但是从开发的角度而言,开发更有效率了,代码可阅读性大大提升。
3)Setter & Getter(如何读写属性)
作用:保护私有变量,通过一定规则来限制对属性的修改。
class Animal{
constructor(type){ //构造函数,里面写属性
this.type=type;
}
get age(){ //这里的 age 不是真正意义上的属性,只是用来读取 age 的入口
return 4;
}
walk(){ //方法
console.log('I am walking');
}
}
let dog=new Animal();
dog.age=5;
console.log(dog.age) //4,get相当于 getter ,只允许读取数据,不允许修改数据;若要修改需使用 set 方法
set 、get 配合使用
let _age = 4
class Animal {
constructor (type) { // 构造函数,里面写属性
this.type = type
}
get age () {
return _age
}
set age (val) {
if (val > 4 && val < 7) {
return _age = val
}
}
walk () { // 方法
console.log('I am walking')
}
}
let dog = new Animal('dog');
console.log(dog.age); //4
dog.age=5;
console.log(dog.age); //5
4)Statuc Methods(静态方法)
类的实例方法:可通过实例去访问的方法;方法依赖于对象的属性或方法时(内部要引用实例对象的信息),需要定义为实例对象方法。
类的静态方法:不属于对象实例,只属于类;不能通过实例去访问该方法,只能通过类去访问;不涉及实例对象的内容时,可以定义为静态方法。
//ES5中利用 function 实现
let Animal=function (type) {
this.type=type
this.walk=function (){
console.log('I am walking')
Animal.eat() //这里因为 eat 是静态方法,无法使用 this.eat() ,因为 this 指的是实例对象
}
}
Animal.eat=function (food){ //这个方法就是静态方法,实例对象无法访问,只能通过 Animal.eat() 访问
console.log('I am eating')
}
let dog=new Animal('dog')
dog.walk() //先后输出 ‘I am walking’,‘I am eating’
dog.eat() //报错:dog.eat is not a function
//eat 是静态方法,只能通过 Animal.eat() 去使用,不能通过实例对象使用
ES6 使用 static
标记是否是静态方法
Class Animal {
constructor(type){
this.type=type
}
walk(){
console.log('I am walking')
}
static eat(){ //静态方法
console.log('I am eating')
}
}
5)Sub Classes(类的继承)
ES5 中的继承
let Animal=function (type) {
this.type=type
this.walk=function (){
console.log('I am walking')
}
}
Animal.prototype.eat=function(){
this.walk()
console.log('I am eat')
}
let Dog=function(){
Animal.call(this,'dog')
//call 方法实现了构造函数中的继承,即此时 Dog 只继承了 Animal 的 type 属性和 walk 方法,没有继承 eat 方法
//第一个参数 this ,将 Animal 中的 this 指向了 Dog 的实例对象
//第三个参数是 Animal 中的 type ,若没有这个参数, 实例对象dog 就没有 type 属性,即 dog.type 为undefined
}
Dog.prototype=Animal.prototype
//这里实现了原型链上的继承,此处表示,Dog 类上,挂载了 eat 方法
let dog=new Dog()
dog.eat();
ES6 中类的继承
class Animal{
constructor(type){
this.type=type
}
eat(){
this.walk()
console.log('Im eat food')
}
walk(){
console.log('I am walking')
}
}
class Dog extends Animal{
constructor(type){
super(type)
this.age=2 //若子类没有增加新的属性,constructor 函数可以省略不写
}
}
var dog=new Dog()
dog.eat()
练习
哪些场景可以用类来实现
阅读
类
Classes(ES6)Sample
ES6 class
Classes
ES6 Class Tutorial
1)默认参数
ES5 默认参数:
function f(x,y,z){
if(y === undefined){
y=7
}
if(z === undefined){
z=42
}
console.log(arguments.length) //这里获取到参数的长度
return x + y + z
}
console.log(f(1))
ES5 的函数中,可以通过 arguments 去获取参数信息,包括接受的参数值、参数长度
ES6 默认参数:
function f(x,y=7,z=43){ //function f(x,y=7,z=x+y){
console.log(f.length) //这里可以获取到没有默认参数的长度
return x + y + z
}
console.log(f(1)) //51
console.log(f(1,2,3)) //6
console.log(f(1,undefined,3)) //11
注:ES6中,函数若有多个参数,如上例所示
1、若要使用参数 y 的默认值,可以在调用函数时,参数写为 undefined ,若 y 、z 都要使用默认值,可直接省略
2、没有默认值的参数,写在前面,需要默认值的参数写在后面,如示例中,x 在 y 和 z 的前面
3、默认值可以是常量,也可以是参数表达式
2)Rest Parameter(不确定参数)
ES5
function f(){
let num=0
//arguments 是伪数组,所以需要转换为数组,再去遍历
//Array.prototype.forEach.call(arguments,function(item){
// num+=item*1
//})
Array.form(arguments).forEach(function(item){
num+=item*1
})
console.log(num)
}
f(1,2,3,4)
ES6
function f(base,...nums){
//...为展开运算符,若没有参数base,nums中包含传入的所以参数,
//在这里,base表示第一个参数,nums表示之后的所有参数
let num=0+base*2
nums.forEach(function (item){ //nums 是数组
num+=item*1
})
console.log(num)
}
f(1,2,3,4,5)
3)Spread Operator(reset 参数的逆运算)
函数定义时参数确定,传入的数据不确定时:
function f(x=1,y=2,z=3){
return x + y + z
}
let data=[4,5,6]
//ES5
console.log(f.apply(this,data)) //16
//ES6
console.log(f(...data)) //16
Spread Operator 和 Rest Parameter 是形似但意义相反的操作符,简单来说,Rest Parameter 是把不定的参数 “收敛” 到数组,而 Spread Operator 是把固定的数组内容 “打散” 到对应的参数。
1)函数声明
ES5
function hello(){
...
}
//或
let hello=function (){
...
}
ES6:
let hello = (name) => {
console.log('hello',name)
}
2)简写
let hello = name => {
console.log('hello',name)
}
let pow = x => x * x
ler person = (name) => ({
age:20,
addr:'Beijing Ciry'
})
3)拓展(this)
ES5 中的普通函数中,有 this
说法,谁调用函数,this
就指向谁,
而箭头函数对 this
的处理是在定义时,this
的指向,之后不做改变
练习:
1、如何用箭头函数来实现一个数组的排序
2、箭头函数对 this 的处理还有什么妙用
阅读:
箭头函数
默认参数
reset-paramrters
Three dots (…) in Javascript
1)定义
ES5
var x=1,y=2,z=3;
var obj={
x:x,
y:y,
f:function(){ //对象中的函数
...
}
}
obj[a]=0 //新增
ES6
let x=1,y=2,z=3;
let obj={
x, //ES6 写法,key 和 value 一样的情况下,可以只写一个
y:y,
[x+y]:3 //可以通过 [] 来新增属性,[]里面可以放键名,也可以是 表达式
f(a,b){ //ES6 对象中的方法可以简写,该方法为同步方法
...
}
* q(a,b){ //带有 * 的为异步方法,后面会学习到;**ES5 中不支持异步函数**
...
}
}
ES6 中的 Set 数据结构对应传统数据结构的 “集合”,类似于数组,但成员的值是唯一的,没有重复。Set 本身是一个构造函数,用来生成 Set 数据结构。
1)生成 Set 实例
let s=new Set() //定义空的 Set 实例
let s=new Set([1,2,3,4]) //实例化的同时传入默认的数据
注:
初始化的参数必须是可遍历的,可以是数组或者自定义遍历的数据结构
2)添加数据
let s=new Set()
s.add('hello')
s.add('world')
//或者
s.add('hello').add('world')
console.log(s)
注:
Set 结构不允许数据重复,所以添加重复的数据是无效的,
如:s.add('hello').add('world').add('hello')
,结果还是上图所示
3)删除数据
let s=new Set(['hello','world'])
s.delete('hello') //返回 true
s.delete('hi') //返回 false
4)清空数据
let s=new Set(['hello','world'])
s.clear()
5)统计数据
let s=new Set(['hello','world'])
s.has('hello') //查询数据,存在返回 true,否则返回 false
console.log(s.size) //2
s.add('hi')
console.log(s.size) //3
注:
这里的 size 是 Set 数据的属性,不是方法
6)查询数据
let s=new Set(['hello','world'])
console.log(s.keys()) //SetIterator {'hello','world'}
console.log(s.values()) //SetIterator {'hello','world'}
console.log(s.entries()) //SetIterator {"hello" => "hello", "world" => "world"}
s.forEach(item => {
console.log(item) //hello//world
})
for(let item of s){
console.log(item) //hello//world
}
注:
SetIterator 后续会学习
思考:
1、因为 Set 中的值总是唯一的,所以需要判断两个值是否相等,那么 +0,-0,NaN,undefined 可以被重复添加吗?
不可以。
2、Set 对存储的类型是不限的,如果想限制只能存储对象,该怎么办?请使用 WeakSet
Map
数据结构,类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object
结构提供了“字符串–值”的对应,Map
结构提供了“值–值”的对应,是一种更完美的 Hash 结构实现。如果需要“键值对”的数据结构,Map
比Object
更合适。
1)生成 Map 实例
//法1:数组
let M=new Map([['name','张三'],['title','Author']])
//参数是数组时,需要数组成员时一个个表示键值对的数组,
//该例中,新建Map实例时,就制定了两个键--name和title
console.log(M) //Map(2) {"name" => "张三", "title" => "Author"}
//法2
let m=new Map()
let o={p:'hello world'}
m.set(o,'content-1')
console.log(m) //见下图
let map=new Map()
let keyObj={}
let keyFunc=function(){}
let keyString='a string'
map.set(keyString,"和键 a string 关联的值")
map.set(keyObj,"和键 keyObj 关联的值")
map.set(keyFunc,"和键 keyFunc 关联的值")
console.log(map)
3)删除数据
代码接 2)
map.delete(keyObj) //返回 true,若map中不包含 keyObj ,则返回false
4)清空数据
代码接 2)
map.clear()
5)统计数据
代码接 2)
console.log(map.size) //3
6)查询数据
get()
方法返回某个 Map 对象中的一个指定元素keys()
方法返回一个新的 Iterator 对象,它包含顺序插入 Map 对象中每个元素的 key 值values()
方法返回一个新的 Iterator 对象,它包含按顺序插入 Map 对象中每个元素的 value 值entries()
方法返回一个新的包含【key,value】对的 Iterator 对象,返回的迭代器的顺序与 Map 对象的插入顺序相同forEach()
方法将会以插入顺序对 Map 对象中的每一个键值对执行一次参数中提供的回调函数for ... of
可以直接遍历每个成员console.log(map.get(keyObj)) //"和键 keyObj 关联的值"
console.log(map.keys()) //MapIterator {"a string", {…}, ƒ}
console.log(map.values()) //MapIterator {"和键 a string 关联的值", "和键 keyObj 关联的值", "和键 keyFunc 关联的值"}
console.log(map.entries()) //MapIterator {"a string" => "和键 a string 关联的值", {…} => "和键 keyObj 关联的值", ƒ => "和键 keyFunc 关联的值"}
map.forEach((value,key,map){
console.log(value,key,map)
})
for([key,value] of map){ //这里循环项要用 [],第一个参数是 key,第二个参数是 value
console.log(key,value)
}
Object 和 Map 的区别
该方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象,它将返回目标对象。实现的是 浅拷贝
语法:
Object.assign(target,…sources)
let target={a:1,b:2}
let source={b:4,c:5}
let newTarget=Object.assign(target,source)
console.log(target) //{a: 1, b: 4, c: 5}
console.log(newTarget) //{a: 1, b: 4, c: 5}
思考:
1、如果目标对象传入的是 undefined 和 null 将会怎样?
2、如果源对象的参数是 undefined 和 null 又会怎样?
3、如果目标对象是个嵌套的对象,子对象的属性会被覆盖吗?
阅读
es6-enhanced-object-literals
es6-features
Object.assign()
阅读资料:
1、内存管理
2、javascript 世界里有哪些元素是可以遍历的
3、如何给数组结构自定义遍历
4、find()和 ES5 中的有什么区别