js包括 core、bom、dom
DOM是一套操作HTML标签的API(接口/方法/属性)
BOM是一套操作浏览器的API(接口/方法/属性)
www.jianshu.com/p/eac7f9dc3…
一、ES5
ECMA Script是一种由ECMA组织(前身为欧洲计算机制造商协会)制定和发布的脚本语言规范 ES5 即ECMAScript5 ,是javascript的语言的标准的一版。
严格模式
总结
- 变量必须声明
- 不能使用eval作为变量和参数 eval会自己创建一个作用域
- 必须使用new 来调用构造函数
1、不使用严格模式时
globalVal = 100
//这个变量是在window下面,是全局变量
使用严格模式
“use strict”
globalVal = 100//这种会报错
var globalVal = 100//必须用var声明
2、带eval的操作会被禁止
不使用严格模式时
var eval = 100
console.log("eval",100)//打印出100
使用严格模式
"use strict"
var eval = 100
function fun(eval){}
//上面两种情况会报错
eval函数的作用:计算某个字符串、将字符串解析出来
www.w3school.com.cn/js/jsref_ev…
不使用严格模式
var a = 100
eval("var a = 200;console.log(a)");
console.log(a);
//200 200
使用严格模式
创造了一个eval的作用域
var a = 100
eval("var a = 200;console.log(a)");
console.log(a);
//200 100
函数中this的引用
function Person(name,age){
this.name = name;
this.age = age;
}
//this->当前实例对象
var p = new Person("tom",20)
console.log(p.name,p.age)//打印出tom,20
//this->windows
Person("tom",20)
console.log(name,age)//打印出tom,20
运行结果
Tom
Tom
undefined
Michael
Person { name: 'Michael' }
使用严格模式
"use strict"
function Person(name,age){
this.name = name;
this.age = age;
}
//this->当前实例对象
var p = new Person("tom",20)
console.log(p.name,p.age)//打印出tom,20
Person("tom",20)
console.log(name,age)//报错,这时函数自调用中的this->undified
Array扩展
1、数组遍历
var array = ['java','php','js','c']
array.forEach(function(item,index){
console.log(item,index)
}
2、数组过滤
var ret = array.filter(function(item){
if(item.length>=3){
return true
}
})
console.log(ret)//打印新数组{java,php}
3、map
var ret2 = array.map(function(item){
return "IT-"+ item
})
console.log(ret2)//打印出新数组{'IT-java','IT-php','IT-js','IT-c'}
Function扩展
js的面向对象是通过function改过来的,之前不是面向对象
var obj = {name:"tom"}
function fun(a,b){
console.log(this,a,b)
}
fun()//window,undified,undified
调用函数的几种方式
//call与apply函数见《前端基础知识》那篇文章
fun.call(obj,1,2)//{name:"tom"},1,2
fun.apply(obj,[1,2])
fun.bind(obj)(1,2)//绑定对象
setInterval(function(){
console.leg(this)
}.bind(obj),1000)//每隔一秒钟打印{name:"tom"}
二、ES6
块作用域
1、let和const
var a = []
for(var i=0;i<10;i++){
a[i] = function(){
console.log(i)
}
}
console.log(a[6])//输出funcion
a[6]()//输出10,因为绑定的i是一个全局变量
这个问题可以用let或者闭包解决
解决方法一 IIFE(立即执行函数表达式)/闭包(具体可看《前端基础知识》)
var a = []
for(var i=0;i<10;i++){
(function(i){//函数内又有一个函数,形成了一个闭包,延长了局部变量的作用范围
a[i] = function(){
console.log(i)
}
})(i)
}
console.log(a[6])//输出funcion
a[6]()//输出6
解决方法二 使用let
var a = []
for(let i=0;i<10;i++){
a[i] = function(){
console.log(i)
}
}
console.log(a[6])//输出funcion
a[6]()//输出6
- js中的变量提升(具体见《前端基础知识》)如果用let,则不存在变量提升
var a = 100
function fun(){
console.log(a)
var a = 200
}
fun()//undefined
- var可以重复定义,let不可以
var a= 100
var a = 200
console.log(a)//200
最开始在严格模式中不允许重复定义,接着出现了let,也不允许重复定义
let a= 100
let a = 200
console.log(a)//报错
- const也不存在变量提升
console.log(MAX)
const MAX=100//报错
const obj = {a:100}
变量的结构赋值
1、数组的结构赋值
以前的写法
var array = [1,2,3]
var a= array[0]
var b = array[1]
var c = array[1]
现在的写法
let [a,b,c] = [1,2,3]
console.log(a,b,c)//1,2,3
let [a,b] = [1,2,3]
console.log(a,b)//1,2
let [a,b,c] = [1,2]
console.log(a,b,c)//1,2,undefined
let [foo,[[bar],baz]] = [1,[[2],3]]
console.log(a,b,c)//1,2,3
...表示数组
let [x,...y] = ["foo","bar","baz"]
console.log(x,y)//foo,["bar","baz"]
let [x,y="b"]=["a"]
console.log(x,y)//a,b
2、对象的解构赋值
let { foo, bar } = { foo: "aaa", bar: "bbb" }
console.log(foo, bar)
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
let { bar, foo } = { foo: "aaa", bar: "bbb" }
console.log(foo, bar)
let { baz } = { foo: "aaa", bar: "bbb" }
console.log(baz)//undefined
let { max, min } = Math//max、min都定义为Max的对象
console.log(max(1, 3))//输出最大值,3
对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者
//变量名和对象属性名不一致
let { bar: baz } = { foo: "aaa", bar: "bbb" }
console.log("baz", baz)//baz bbb
console.log("bar", bar)//报错:bar is not defined
//默认值设定
let { x = 3 } = {}
console.log(x)
3、字符串的解构赋值
let [a, b, c, d, e] = "hello"
console.log(a, b, c, d, e)//h e l l o
//对象赋值为字符串时,对象为字符串的长度
let { length: len } = "hello"
console.log(len)//5
4、函数的解构赋值
function add([x, y]) {
return x + y
}
console.log(add([1, 3]))//4
let arr = [[1, 2], [3, 4]]
var arr2 = arr.map(function ([a, b]) {
return a + b
})
console.log(arr2)//[3,7]
字符串的扩展
1、模板字符串
let name = "tom"
let str = 'hello' + name
console.log(str)
let str2 = "hello"${name}
console.log(str2)//hello tom
let str3 = "hel
lo${name}"//回车也会显示
function fun(){
return "Hello World"
}
let str = 'foo ${fun()} bar'
console.log(str)//将函数拼接
不使用模板字符串
let emps = [{name:"tom",age:30},{name:"jack",age:40}]
let innerHTML = "<table border=\"1\" width=\"300px\">"
emps.forEach(function(emp){
innerHTML +="<tr><td>"+emp.name+"</td><td>"+emp.age+"</td></tr>"
})
innerHTML += "</table>""
document.write(innerHTML)
使用模板字符串时
let emps = [{name:"tom",age:30},{name:"jack",age:40}]
let innerHTML = `<table border="1" width="300px">
${emps.map(function(emp){
return `<tr><td>${emp.name}</td><td>${emp.age}</td></tr>`
}).join("")}
</table>`
document.write(innerHTML)
2、标签模板
let a = 5
let b = 10
function tag(stringArr,value1,value2){
console.log("stringArr",stringArr)
console.log("value1",value1)
console.log("value2",value2)
}
tag `hello $(a+b) world ${a*b}!`
//{hello ,world ,!}, 15,50
原本的字符串传入第一个,另外两个模板字符串传入另外两个参数
通过函数对模板字符串进行处理,过滤掉不安全的东西
let course = "<script>alert('xxx')<\/script>"
function safeHTML(data,params){
console.log(arguments)//所有的参数
console.log(data)
console.log(params)
let s = data[0]
for(let i = 1;i<arguments.length;i++){
let arg = arguments[i]
s+=arg.replace(/&/g,"&")将&,>,<替换为html中的转义字符,/g表示全局
.replace(/</g,"<")
.replace(/>/g,">")
s+=data[i]
}
return s
}
let strHTML2 = safeHTML `<p>我们要开始学习${course}的课程了</p>`
document.write(strHTML2)
函数的扩展
1、函数参数的默认值
function fun(x,y="world"){
console.log(x,y)
}
fun("hello")//hello,world
fun("hello","JS","xx")//hello ,JS
与解构赋值结合使用
function fun1({x,y=5}={}){//传入的参数是一个对象,给一个对象赋予一个默认值
console.log("fun1",x,y)
}
fun1({})//undefined,5
fun1({x:1})//1,5
fun1({x:1,y:2})//1,2
2、rest参数
//将函数多余的参数,放入到rest参数对应的数组中
function add(...values){//这是rest参数的写法,rest参数只能放在最后一个参数的位置写
console.log(values)
let sum = 0
for(let i = 0;i < values.length;i++){
sum += values[i]
}
return sum;
}
console.log(add(1,2,3,4,5))
www.jianshu.com/p/50bcb376a… rest参数与arguments对象的区别
3、箭头函数
箭头函数的this指针见《前端基础知识》
var f1 = function(v){
return v
}
console.log(f1(5))//5
var f2 = v => v //与f1相同
console.log(f2(5))//5
//函数没有参数,用()代表函数参数部分
var f3= () => 5
//函数有多个参数,用()代表函数参数部分
var f4 = (a,b) => a+b
console.log("f4()",f4(1,2))//f4() 3
var f5 = (a,b)=>{
var sum = a+b
return sum
}
console.log("f5()",f5(1,2))//f5(),3
//返回结果为对象时,必须在对象外面加上一个括号
var f6=() => ({name :"tom"})
//对象作为形参时,解构赋值
var f7 = ({a,b}) => a + "," + b
console.log("f7",f7({a:1,b:2}))//f7,1,2
var f8 = (...numbers) =>numbers
console.log(f8(1,2,3,4))//1,2,3,4
下面两个函数相同
var arr1 = [1,2,3].map(function(x){
return x*2
})
var arr2 = [1,2,3].map(x => x*2)
4、箭头函数中this的引用
具体见《前端基础知识》
function Person(name,age){
this.name = name;
this.age = age;
//setInterval函数是由windows调用的
setInterval(function(){
console.log(this,name,age)//windows,name,age
},1000)
}
function Person(name,age){
this.name = name;
this.age = age;
let _this = this
//这样子可以通过this调用
setInterval(function(){
console.log(this,this.name,this.age)//windows,name,age
},1000)
}
function Person(name,age){
this.name = name;
this.age = age;
//箭头函数没有自己的this,他的this是继承而来,默认指向定义它的所处的对象(宿主对象)
setInterval(() => {
console.log(this,this.name,this.age)
},1000)
}
let p1 = new Person("tom","12")
箭头函数不能当作构造函数使用,不可以使用new命令
let fun(){
console.log("fun is called")
}
let f = new fun()//报错
let fun(){
//不可以使用arguments,最好使用rest参数
console.log(aruguments)
console.log("fun is called")
}
fun()
数组的扩展
1、扩展运算符
//扩展运算符,是rest参数的逆运算
//将一个数组中的数据,转为参数的序列
console.log(...[1,2,3])//1,2,3
function add(x,y){
return x + y
}
let params = [4,5]
//add(params[0],params[1])
console.log(add(...params))//9
数组的复制
let a1 = [1,2]
let a2 = a1 //相当于将a1的地址给了a2
a2[0] = 2
console.log("a1",a1)
console.log(a1 == a2)//true
//使用concat方法,将两个数组合并,并返回一个新的数组
let a1 = [1,2]
let a2 = a1.concat()
console.log(a2)
console.log(a1 == a2)//false,因为a2是一个新的数组
let a1= [1,2]
let a2 = [...a1]
console.log(a1 == a2)//false
数组的合并
let a1 = ["a","b"]
let a2 = ["c"]
let a3 = ["d","e"]
let a4 = a1.concat(a2,a3)
console.log("a4",a4)//[a,b,c,d,e]
let a5 = [...a1,...a2,...a3]
console.log(a5)//[a,b,c,d,e]
如果数组里面是对象
let obj1 = {foo:1}
let obj2 = {bar:1}
let array1 = [obj1]
let array2 = [obj2]
let array3 = array1.concat(array2)
let array4 = [...array1,...array2]
console.log(array3)//[{foo:1},{bar:1}]
console.log(array4)//[{foo:1},{bar:1}]
console.log(array3[0] == array1[0])//true.因为这是浅复制,即只指向了相同的地址,如果修改array3,则也会修改掉array1
console.log(array4[0] == array1[0])//true
与解构赋值结合
let [first,...rest] = [1,2,3,4,5]
let a1 = [..."Hello"]
console.log(a1)//["H","e","l","l","o"]
对象的扩展
1、对象属性的简介表示
var obj = {name:'',age:19}
//两种获取属性的方法
obj.name = "jack"
console.log(obj.name)
obj["name"] = "mike"
console.log(obj["name"])
必须要用[]获取属性的情况
1、属性是变量
let param = "gender"
obj.param = "male"
console.log(obj)//{..... param:male},这是有问题的
let param = "gender"
obj[param] = "male"
console.log(obj)//{.... gender:"male"}
2、属性中包含特殊字符
obj.x-z="xxxx"//会报错
obj["x-z"] = "xxxx"//成功
let foo = "bar"
let baz1 = {foo : foo}
console.log(baz1)//{foo:"bar"}
简略的写法
let baz1 = {foo}
console.log(baz1)//{foo:"bar"}
function fun1(x,y){
return {x:x , y:y}
}
console.log(fun1(1,2))//1,2
简单写法
function fun1(x,y){
return {x , y}
}
console.log(fun1(1,2))//1,2
2、属性名表达式
let obj1 = {}
onj1.foo = true
obj1."a"+"bc" = 123//错误,这种形式不支持表达式
obj1["a" + "bc"] = 123//正确
console.log(obj1)//{foo:true , abc:123}
函数名也可以为表达式
let obj3 = {
["a" + "bc"]() {
}
}
3、对象方法的简洁表示
let obj1 = {
fun:function (){
return "hello"
}
}
console.log(obj1.fun())
简单写法
let obj2 = {
fun(){
return "hello"
}
}
console.log(obj1.fun())
【练习】
1、编写一个js函数,实现对一个数组去重的功能
let a = (tmps)=>{
let arr = []
for(let i = 0; i < tmps.length;i++){
if(arr.indexOf(tmps[i])==-1){//如果tmps中的元素没有在arr中出现过,则加入arr
arr[i] = tmps[i]
}
}
return arr
}
console.log(a([1,2,3,4,1,2,3]))
2、编写一个js函数统计字符串中每个字符出现的频率
Set和Map数据结构
1、set的基本使用 set类似数组,但成员的值都是唯一的,不会有重复
let s = new Set()
let arr = [1,2,3,3,2,1]
arr.forEach(x = > s,add(x))
console.log(s)//[1,2,3]
set的遍历
for(let i of s){
console.log(i)
}
//array转为set
let s = new Set([1,2,3,3,2,1])
//set再转为array
let array = [...s]//[1,2,3],这样子也可以去重
console.log([...new Set("aabbcc")].join(""))//abc
let s = new Set()
let a = 1
let b = "1"
s.add(a)
s.add(b)
//可以加进去,说明set中判断两个元素是否相同采用的是“===”,因此1和“1”都可以放进去
let c = {}
let d = {}
s.add(c)
s.add(d)
//也可以加进去,因为他们两个地址不同
set的方法
let s = new Set()
s.add(1).add(2).add(3)
console.log(s.size)//3
console.log(s.has("1"))//false
s.delete(2)
console.log(s.has(2))//false
//array转为set
let items = new Set([1,2,3])
console.log(items instanceof set,items instanceof Array)//true,false
//set转为array
let array = Array.from(items)
console.log(array instanceof Set,array instanceof Array)
因此上面的练习题
**“编写一个js函数,实现对一个数组去重的功能”**可以写成下面的代码
function change(array){
return Array.from(new Set(array))
}
set没有key,只有value(key = value)
let s = new Set(["red","green","blue"])
for(let item of s.keys){
console.log(item)//"red","green","blue"
}
for(let item of s.values){//这里的values可以省略
console.log(item)//"red","green","blue"
}
for(let item of s){//这里的values可以省略
console.log(item)//"red","green","blue"
}
//entries 是key和value的集合
for(let item of s.entries){
console.log(item)//{"red","red"},
{"green","green"},{"blue","blue"}
}
s.forEach((v,k) => console.log(k,v))//red red
green green blue blue
2、Map的基本使用
Map与对象类似,也是键值对的组合
Map 键的范围不限于字符串,可以是各种数据类型
let m = new Map()
let o = {a: "hello"}
m.set( o,"content" )
console.log(m) //key:{a: "hello"} value:"content"
console.log(m.get(o))// 获取value值 输出content
Map与二维数组的转换
let map = new Map([["name","tom"],["age",18]])
console.log("map",map)
//{"name":"tom"},{"age":18}
Set与Map的转换
let set = new Set([["foo",1],["bar",2])
let m = new Map(set)
console.log("m",m)
Map本身作为key,结果与他自己相同
let m = new Map([["baz",3]])
let m1 = new Map(m)
console,log(m1) // baz:3
let map = new Map()
map.set("foo",true)
map.set("bar",false)
console.log(map.size) //2
console.log(map.get("foo")) //true
console.log(map.has("foo")) //true,这个key存在
console.log(map.delete("foo")) // true 删除也会返回true或者false
map.clear()
console.log(map.size) // 0
let m = new Map([["foo","no"],["bar","yes"]])
for(let key of m.keys()){
console.log(key) //foo bar
}
for(let value of m.values()){
console.log(value) //no yes
}
for(let item of m.entries()){
console.log(item) //["foo","no"],["bar","yes"]
}
//解构赋值
for(let [key,value] of m.entries()){
console.log(key,value) //foo no bar yes
}
//map->array
console.log([...m.keys()]) //返回所有的key
console.log([...m.values()]) //["no","yes"]
console.log([...m.entries()]) // [["foo","no"],["bar","yes"]]
console.log(...m) //等同于[...m.entries()]
class类和对象
1、Class基本语法
let p = new Object() //无法判断类型
//自定义构造函数
function Person(name,age){
this.name = name
this.age = age
this.printInfo = function(){
console.log(this.naem)
console.log(this.age)
}
}
let p = new Person("tom",18)
p.printInfo() // tom 18
console.log(p)
可以发现 name,age,printInfo()全部挂载在了p上面,当对象变多时,每个对象上都有printInfo(),非常占内存
JS中通过原型链解决这个问题(具体看《前端基础知识》)
- 将属性定义在构造函数中,将函数定义在原型上,这样可以减少内存
ES5解决方法如下
function Person(name,age){
this.name = name
this.age = age
}
Person.prototype.printInfo = function(){
console.log(this.naem)
console.log(this.age)
}
let p = new Person("tom",18)
p.printInfo() // tom 18
在ES6中提供了类,可以看作是一个语法糖
class Person {
constructor(name,age){
this.name = name;
this.age = age;
}
//在class内定义的方法,其实都是定义在其原型对象上的
printInfo(){
console.log(this.name)
console.log(this.age)
}
}
let p = new Person("tom",18)
console.log(p) //可以看到printInfo也是在_proto_里面的,与上面的原型方法相同
console.log(typeof Person ) //function
console.log(Person.prototype.constructor == Person) // true
2、constructor构造函数
-
通过new 构造对象时,会自动调用构造函数
-
通常在构造函数中,进行一些数据的初始化
-
在一个类中必须要有一个构造函数,如果没有被显式定义,默认会添加一个空的constructo
class Person{
}
let p1 = new Person()
- this指向当前创建的实例对象
class Person {
constructor(){
console.log(this) //Person()
}
}
let p = new Person()
- 与Java不同,js中在一个class中只能有一个构造函数 以下代码会报错
class Person {
constructor(){
console.log(this) //Person()
}
constructor(name,age){
this.name = name
this.age = age
}
}
let p = new Person()
let p2 = new Person("tom",13) //会报错,因为有两个构造函数
class Person {
constructor(){
console.log(this) //Person()
}
}
let p = new Person()
let p2 = new Person("tom",13) // 不会报错,仍然会调用constructor,只不过参数没有被接受
class Person {
constructor(...params){
console.log(this) //Person()
console.log(params)
}
}
let p = new Person() //params为空
let p2 = new Person("tom",13) // params为 tom,13
3、属性表达式
类的属性也可以用表达式方法
let methodName = "print"
class Person {
[methosName]() {
console.log(methosName + "is called")
}
}
let p = new Person()
console.log(p)
p[methosName]() // 因为是变量,所以要用[]来调用
4、Class表达式
let Myclass = class Me{
getClassName(){
return Me.name
}
}
let myClass = new Myclass()
console.log(myClass.getClassName()) //Me
console.log(Me.getClassName()) //报错
立即执行class,在定义class的同时创建出实例对象
//一般在只该类使用一次的情况下使用
let person = new class{
constructor(name,age){
this.name = name
this.age = age
}
printInfo(){
console.log(this.name)
console.log(this.age)
}
}("tom",18)
5、static静态方法
在类中定义的方法前加上static关键字,该方法则为静态方法
class Foo {
//实例方法,定义在实例的原型对象上
methodA() {
console.log("methodA is called")
}
//静态方法
static methodB(){
console.log("methodB is called ")
}
}
let foo = new Foo()
console.log(foo) //methodA在实例原型上,但methodB不在
//静态方法直接通过类访问,和实例对象无关
Foo.methosB()
class Foo
//实例方法,定义在实例的原型对象上
methodA() {
console.log("methodA is called")
}
static methodB(){
console.log("methodB is called ")
}
static bar (){
console.log(this) //this->类,而不是实例
this.baz()
}
static baz(){
console.log("baz is called")
}
}
let foo = new Foo()
foo.methodA()
foo.methodB()
Foo.bar() // baz is called
6、class的继承
继承实际上就是将父类放到了子类的原型链中
class Person{
constructor(name,age){
this.name = name
this.age = age
}
printInfo(){
console.log(this.name)
console.log(this.age)
}
}
class Student extends Person{
constructor(name,age,major){
super(name,age) //里面的this指向子类
this.major = major
}
study(){
console.log("好好学习")
}
printInfo(){
console.log(this.name)
console.log(this.age)
console.log(this.major)
}
}
let s1 = new Student("tom",18,"IT")
console.log(s1)
s1.printInfo() // tom 18 IT ,调用子类的函数
s1.study() //调用子类的函数
promise
async和await
补充
1、setTimeout(function(){},0)
www.cnblogs.com/xyy2019/p/1…
setTimeout是异步的注册事件,它有两个参数,第一个参数是函数,第二参数是时间值。调用setTimeout时,把函数参数,放到事件队列中。等主程序运行完,再调用。 就像我们给按钮绑定事件一样。
2、正则表达式与test函数
www.runoob.com/regexp/rege…
通过正则表达式与test函数可以判断字符是否为字母或数字
正则表达式中的^的意思
只要是”^”这个字符是在中括号“[]”中被使用的话就是表示字符类的否定,如果不是的话就是表示限定开头,即/^A/会匹配"An e"中的A,但是不会匹配"ab A"中的A,所以如果使用正则表达式判断一个字符串中是否包含字母时,不能用^,而应该直接写为/[A-Za-z]/
blog.csdn.net/sufubo/arti…