JAVASCRIPT

# 一、自理

<script></script>

写在head或者body标签内

<script src= “xxxxx.js"></script>

alter(“警告”);弹窗显示

prompt(“请输入密码”,“”);弹窗显示加一个输入框

document.write(“打印”);打印文字

# 二、undefined和null

console.log(undefined == null) //true

使用typeof运算时“null”,返回object

1】一般情况下undefined的是变量声明了,没有赋值

2】null一般可以用来内存垃圾回收机制,让之后不用的变量指向为空,释放内存

# 三、变量存储的本质

# 1】内存的分类:栈空间和堆空间(堆空间有内存地址)

①基本类型变量在第一次赋值被分配到栈空间

var name="kobe";//被分配到栈空间,此时name指向栈空间的"kobe"
name="leihao";//此时栈空间的"kobe"直接被修改成"leihao"

②对象(引用)类型变量第一次赋值被分配到堆空间(有内存地址)

对象类型变量都是内存地址,并不是真正的对象值

var info={name:"kobe"}//第一次赋值是把{name:"kobe"}放到堆空间中(它有一个内存地址,info指向内存地址)

# 四、三元运算符

之所以叫三元是因为整个表达式中有三个元素。

三元表达式是一种 if else 的简便写法。

//三元表达式的格式:
表达式1 ? 表达式2 : 表达式3

判断表达式1是否为真,为真执行表达式2,否则为假执行表达式3

# 五、ES6扩展运算符

作用:将一个数组转为用逗号分隔的参数序列。

//该运算符主要用于函数调用。
function push(array, ...items) {
array.push(...items);
}
function add(x, y) {
return x + y;
}
var numbers = [4, 38];
add(...numbers)  // 42
//扩展运算符取代apply方法的一个实际的例子,应用Math.max方法,简化求出一个数组最大元素的写法。
// ES5的写法
Math.max.apply(null, [14, 3, 77])
// ES6的写法
Math.max(...[14, 3, 77])
// 等同于
Math.max(14, 3, 77);

//通过push函数,将一个数组添加到另一个数组的尾部
// ES5的写法
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
Array.prototype.push.apply(arr1, arr2);
// ES6的写法
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
arr1.push(...arr2);

//合并数组
// ES5的写法
[1, 2].concat(more)
// ES6的写法
[1, 2, ...more] 
var arr1 = ['a', 'b'];
var arr2 = ['c'];
var arr3 = ['d', 'e'];
// ES5的合并数组
arr1.concat(arr2, arr3);// [ 'a', 'b', 'c', 'd', 'e' ]
// ES6的合并数组
[...arr1, ...arr2, ...arr3]// [ 'a', 'b', 'c', 'd', 'e' ]

扩展运算符将字符串转为真正的数组
[...'hello']
// [ "h", "e", "l", "l", "o" ]

# 六、冒泡排序

const arr=[14,3,151,46,12,9,16,23,5,74]
for(var i=arr.length-1;i>0;i--){
    for(var j=0;j<i;j++){
        if(arr[j]>arr[j+1]){
            var temp=arr[j]
            arr[j]=arr[j+1]
            a[j+1]=temp
           }
    }
}

# 七、ES6变量let和var

# ①只在let命令所在的代码块内有效。
{
  let a = 10;
  var b = 1;
}
a // ReferenceError: a is not defined.
b // 1
# ②只在for循环体内有效,在循环体外引用就会报错
for (let i = 0; i < 10; i++) {
  // ...
}

console.log(i);
// ReferenceError: i is not defined

下面的代码如果使用var,最后输出的是10

var a = [];
for (var i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 10

上面代码中,变量ivar命令声明的,在全局范围内都有效,所以全局只有一个变量i。每一次循环,变量i的值都会发生改变,而循环内被赋给数组a的函数内部的console.log(i),里面的i指向的就是全局的i。也就是说,所有数组a的成员里面的i,指向的都是同一个i,导致运行时输出的是最后一轮的i的值,也就是 10。

如果使用let,声明的变量仅在块级作用域内有效,最后输出的是 6。

var a = [];
for (let i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 6

上面代码中,变量ilet声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后输出的是6。你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。

# 八、立即执行函数

表达的含义是一个函数定义完后被立即执行

第一部分定义了一个匿名函数,这个函数有自己独立的执行上下文环境。

第二部分是后面的(),表示这个函数被执行了

(function(){
    console.log("立即执行函数被执行")
})()
# 作用:会创建一个独立的执行上下文环境,可以避免外界访问或修改内部的变量,也避免了对内部变量的使用
//匿名函数表达式,会立即被执行
var demo=function(){
    console.log("demo")
}()
//而下面这样是不会被执行的,是错误的,只有函数表达式才会立即执行
function(){
    console.log("demo")
}()
//如果需要他立即执行可以把它外层加一个(),加()就认为是整体是表达式
(function(){
    console.log("demo")
}())

所以立即执行函数就有两只写法:执行后函数立即被销毁

//写法一:
(function(){
    console.log("立即执行函数被执行")
})()
//写法二:
(function(){
    console.log("demo")
}())

# 九、参数的传递(变量基本类型和引用类型区别)

总结:如果是基本变量类型一般都需要return,引用变量类型就不需要return

①首先我们要明白ECMAScript中所有参数传递都是值,不可能通过引用传递参数

下面这种基本变量类型很好理解,传入进去的是一个a变量对应的值,在函数里面对应执行打印出的结果都很能理解,函数执行完毕之后函数立即被销毁,如果不在函数内部进行return或者set操作是不会改变函数以外的结果的,所以此时最后的打印依然是原来的值。

function text(tem){
				console.log(tem)//leihao等同于tem="leihao"其中a的值直接给了参数
				tem="name"
				console.log(tem)//name
			}
			var b="leihao"
             var a=b
			text(a)//此时a和b都是指向同一个栈空间"leihao",属于基本变量类型,参数是值传递,a的值直接给了参数,并不是把a给了参数
			console,log(a)//leihao

①数组和对象都是引用变量类型

首先我们要明白:数组和对象其实是存在于堆空间的,引用变量只是保存他们处于堆空间的地址来指向这些数组和对象。

下面这个例子,就印证了ECMAScript中所有参数传递都是值,虽然向函数传递的参数是引用变量类型,但是传递进入函数的是在栈空间的变量(这个变量也就是指向对应堆空间的地址),在函数内部对这个变量进行赋值操作,并没有影响到堆空间的数据,只是影响到了一个引用。所以这种情况可以和上面第一种情况当成一回事。

	function text(tem){
				console.log(tem)//{name:"leihao"}
//				tem.name="name"
				tem={name:"name"}
				console.log(tem)//{name:"name"}
			}
			var b={name:"leihao"}
		
			text(b)
				console.log(b)//{name:"leihao"}

在函数中通过**“对象.属性”的操作,就相当于在操作堆空间了,所以影响到了函数以外的变量的指向地址的值,虽然函数执行完毕被销毁了,但是引用变量指向的地址的值发生了改变**,所以此时函数里不需要return或者set操作就会影响到函数外部。

下面是打印结果:

	function text(tem){
				console.log(tem)//{name:"leihao"}
        	 tem.name="name"
//			tem={name:"name"}
				console.log(tem)//{name:"name"}
			}
			var b={name:"leihao"}
		
			text(b)
				console.log(b)//{name:"name"}函数外的变量被改变

# 十、JavaScript Event Loop

JavaScript是一门单线程的语言,它的异步和多线程的实现是通过event loop事件循环机制来实现的大体有三个部分组成,调用栈(call stack)、消息队列(Message Queue)、微任务队列(Microtask Queue)

优先级是调用栈>微任务>消息队列

# ①全是同步方法
function func1(){
    cosole.log(1);
}
function func2(){
    console.log(2);
    func1();
    console.log(3);
}
func2();

上面代码的事件循环是(以下"压入栈"用"入"代替,"弹出栈''用"出"代替):func2()入,console.log(2)入,console.log(2)出,func1()入,cosole.log(1)入,cosole.log(1)出,func1()出,console.log(3)入,console.log(3)出,func2()出。

# ②加入异步操作,如:setTimeout(),setInterval()等

它们中的回调函数会入队到消息队列中,称为"消息",消息会在调用栈清空时执行。

function func1(){
    cosole.log(1);
}
function func2(){
    setTimeout(()=>{
        console.log(2);
    },0);
    func1();
    console.log(3);
}
func2();

上面代码的事件循环是:func2()入, setTimeout(()=>{console.log(2);},0)入,console.log(2)进入消息队列(回调函数进入消息队列),setTimeout(()=>{},0)出, func1()入, cosole.log(1)入, cosole.log(1)出, func1()出,console.log(3)入,console.log(3)出,func2()出,console.log(2)入,console.log(2)出。

# ③使用Promise、async/await创建的异步操作会加入到微任务队列中。

当调用栈清空的时候就会立即执行,优先级高于"消息"

var p=new Promise(resolve=>{
    console.log(4);
    resolve(5);
});
function func1(){
    cosole.log(1);
}
function func2(){
    setTimeout(()=>{
        console.log(2);
    },0);
    func1();
    console.log(3);
    p.then(resolved=>{
       console.log(resolved) 
    }).then(()=>{
        console.log(6)
    });
}
func2();

上面代码的事件循环是:new Promise()首先被压入调用栈,console.log(4)入,console.log(4)出,resolve(5)入,resolve(5)出,func2()入, setTimeout(()=>{console.log(2);},0)入,console.log(2)进入消息队列(回调函数进入消息队列),setTimeout(()=>{},0)出, func1()入, cosole.log(1)入, cosole.log(1)出, func1()出,console.log(3)入,console.log(3)出,p.then(resolved=>{ console.log(resolved)})和then(()=>{console.log(6)})两个回调函数会入队到微任务队列中,func2()出,一旦调用栈清空就执行微任务队列中的事件,console.log(resolved)入,console.log(resolved)出,console.log(6)入,console.log(6)出,再执行消息队列中的消息console.log(2)入,console.log(2)出

# 十一、函数闭包

闭包也差不多就是函数return 另一个函数,这个函数执行时还可以使用之前函数作用域里的变量

闭包就是能够读取其他函数内部变量的函数,由于JavaScript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成“定义在一个函数内部的函数”。所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁

作用:闭包可以用在许多地方。它的最大用处就有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中重用变量又不能造成全局污染

1.隐藏变量2.不会被变量垃圾回收

在这里插入图片描述

# 十二、函数和变量的提升

var变量可以在js中先使用再声明

x=5;
console.log(x);//打印出结果5
var x;

函数提升,在函数声明之前调用函数

console.log(divide(4,2));//结果就是2
function divide(a,b){
    return a/b;
}

# 十三、argument

argument是函数里面内置的跟数组类似的参数集合

img

# 特点

1.arguments对象和Function是分不开的。

2.因为arguments这个对象不能显式创建。

3.arguments对象只有函数开始时才可用。

# 使用方法

虽然arguments对象并不是一个数组(类数组),但是访问单个参数的方式与访问数组元素的方式相同

例如: arguments[0],arguments[1],。。。arguments[n]; 在js中 不需要明确指出参数名,就能访问它们

例如:

function test() {
        var s = "";
        for (var i = 0; i < arguments.length; i++) {
            alert(arguments[i]);
            s += arguments[i] + ",";
        }
        return s;
}
test("name", "age");

输出结果:
name,age

# 十四、柯里化

说的是把一个接受多个参数的函数,变成一系列接受一个参数的内部函数

示例:

function addThreeNums(a,b,c){
    return a + b + c;
}
console.log(addThreeNums(1,2,3));//6

//进行柯里化
function addThreeNumsCurry(a){
    return function(b){
        return function(c){
            return a + b + c;
        }
    }
}
console.log(addThreeNumsCurry(1)(2)(3));//6

var fixedTwo = addThreeNumsCurry(1)(2);
console.log(fixedTwo(4))//7

# 十五、回调函数

回调函数是一段代码执行完之后要调用的函数,一般这种函数是作为另一个函数的参数传递进去,再到另一个函数中去调用它。

function request(cb){
     console.log("请求数据")
     cb('success');//可以向回调函数中传参
     console.log('请求结束')
 }
function callback(res){
    console.log('执行回调:'+ res)
}
request(callback);

//另外也可以使用箭头函数来使用回调
function request(cb){
     console.log("请求数据")
     cb('success');
     console.log('请求结束')
 }
request(res=>{//可以向回调函数中传参
    console.log('执行回调:'+res)
});

# 十六、数组

# 1】创建数组
var arr1=[1,2,3]
var arr2=new Array(4,5,6)
var arr3=Array(7,8,9)
var arr4=Array.of(10,11,12)
console.log(arr4)//[10,11,12]

//当使用Array和new Array构造数组时传入一个参数时很特殊,用法是:创建的数组内容为空,传入的唯一参数是指数组长度(length)
var arrSingle=Array(6)
console.log(arrSingle)//[empty x 6]
var arrSingle2=new Array(7)
console.log(arrSingle2)//[empty x 7]

# 2】访问数组
var arr =[1,2,3,4]
console.log(arr.length)//4
console.log(arr[0])//1
# 3】添加元素
var arr=[1,2,3]
arr[0]=4
console.log(arr)//[4,2,3]
arr[3]=5
console.log(arr)//[4,2,3,5]
arr[8]=9
console.log(arr)//[4,2,3,5,empty x 4,9]
# 4】删除元素
var arr =[1,2,3]
arr.length=0
console.log(arr)//[]
//splice的作用是删除和插值
var arr=[1,2,3,4,5,6]
arr.splice(2,1)//第一个参数是index,第二个参数是删除个数
console.log(arr)//[1,2,4,5,6]
arr.splice(2,3,3,4,5,6)//非第一第二个参数以外的参数都是,删除了之后需要插入的值
console.log(arr)//[1,2,3,4,5,6]
# 5】数组遍历
var arr=[1,3,5,7,9]
for(let i=0;i<arr.length;i++){
    console.log(arr[i])//依次分别打印1 3 5 7 9
}
for(let value of arr){
    console.log(value)//同上,依次分别打印1 3 5 7 9
}
//内部传入回调函数,这个回调函数,默认传入三个参数item,index,self
arr.forEach((value,index,self)=>{
    console.log(value,index,self)//1 0 [1,3,5,7,9]...9 4 [1,3,5,7,9]
})
# 6】栈模式
var stack=[1,2,3]
stack.push(4)//在尾巴添加元素,参数是需要添加的值
console.log(stack)//[1,2,3,4]
stack.pop()//删除尾部的一个元素,并返回删除的元素值
console.log(stack.pop())//4
console.log(stack)//[1,2,3]
console.log(stack.peek())//查看栈顶元素,3
console.log(stack[stack.length-1])//3
# 7】队列模式
var queue=[1,2,3]
queue.push(4,5,6)
console.log(queue)//[1,2,3,4,5,6]

queue.shift()
console.log(queue.shift())//删除并返回头部元素,1
console.log(queue)//[2,3,4,5,6]

queue.unshift(10,11,12)//从数组头部开始添加元素
console.log(queue)//[10,11,12,2,3,4,5,6]
# 8】反转数组
var arr=[1,2,3]
console.log(arr.reverse())//[3,2,1]改变了原数组并返回结果
console.log(arr)//[3,2,1]

console.log("hello".split("").reverse().join(""))//字符串olleh
# 9】数组的排序

记住:sort((a,b)=>a-b)升序 sort((a,b)=>b-a)降序

var arr = [1,5,3,2,4]
arr.sort();//执行改变并返回排序后的结果
console.log(arr)//[1,2,3,4,5]

//自定义排序sort((a,b)=>{})其中参数a是数组中前一个元素,参数b是数组中后一个元素
arr.sort((a,b)=>{
    if(a>b){//表示降序
        return -1;//sort()反常的情况降序序就是return负数
    } else if (a<b){//表示升序
        return 1;//sort()默认的情况升序就是return正数
    } else {
        return 0;//相同的话,位置不变
    }
})
//优化代码
arr.sort((a,b)=> b - a);//和上面相同
console.log(arr)//[5,4,3,2,1]
# 10】数组的连接
var arr1=[1,2,3]
var arr2=[4,5,6]
console.log(arr1.concat(arr2))//[1,2,3,4,5,6]
# 11】数组的裁切

只包括第一个参数指定的值,不包括第二个参数指定的值

var arr=[1,2,3,4,5]
//第一个参数是索引值:从第几位开始裁到尾巴
console.log(arr.slice(1))//[2,3,4,5]

var arr1=[1,2,3,4,5]
//第二个参数是指结束索引的值,但是裁剪不包含指定的结束的位置的值
console.log(arr1.slice(1,3))//[2,3]

var arr1=[1,2,3,4,5]
//到倒数一个元素位置,但不包括尾巴指定的值
console.log(arr1.slice(1,-1))//[2,3,4]
# 12】数组的map

使用map()的数组会对数组里面的元素进行迭代,与此同时还会对这些元素进行处理,处理完之后再返回一个数组

[].map()内部传入回调函数,这个回调函数默认传入三个参数item,index,self

const arr=["110","120","130"]
arr.map(console.log)
arr.map((item,index,self)=>{
    console.log(item,index,self)
})
//上面两个结果都是一样的
var arr=[1,2,3,4]
//arr.map()使用map方法时,可以向map()方法中选传三个参数(item,index,self)
var mappedArr=arr.map(item=>item*2)
console.log(mappedArr)//[2,4,6,8]
console.log(arr)//[1,2,3,4]map操作不改变原来的数组
# 13】reduce数组

arr.reduce((previous,current,index,self)=>{},initialValue)

回调函数可接受四个参数:第一个上一次计算的结果或者是提供的初始值;第二个是当前遍历到的数组元素;第三个是当前遍历到的索引;第四个是数组本身

var arr=[1,2,3,4]
var res=arr.reduce((previous,current)=>previous+current,0)
console.log(res)//10
var res2=arr.reduce((first,second)=>first+second)
console.log(res2)//10
var arr=[1,2,3,4]
var res2=arr.reduce((first,second,index)=>{
    console.log(first,second,index)
    //1 2 1
    //3 3 2
    //6 4 3
    return first+second
})
console.log(res2)//10

实现数组去重:

let arr = [1, 1, 2, 3, 4, 3]
let newarr = arr.reduce((first, second) => {
    if (!first.includes(second)) {
        first.push(second)
    }
    return first
}, [])
console.log(newarr)//[1,2,3,4]

JS元素实现reduce方法:

Array.prototype.my_reduce = function (callback, initialValue) {
  if (!Array.isArray(this) || !this.length || typeof callback !== 'function') {
    return []
  } else {
    // 判断是否有初始值
    let hasInitialValue = initialValue !== undefined;
    let value = hasInitialValue ? initialValue : this[0];
    for (let index = hasInitialValue ? 0 : 1; index < this.length; index++) {
      const element = this[index];
      value = callback(value, element, index, this)
    }
    return value
  }
}

let arr = [1, 2, 3, 4, 5]
let res = arr.my_reduce((pre, cur, i, arr) => {
  console.log(pre, cur, i, arr)
  return pre + cur
}, 10)
console.log(res)//25
# 14】数组过滤

使用filter()方法和forEach以及map方法一样方法内部传入回调函数,这个回调函数默认传入三个参数item,index,self,这个回调函数会返回一个Boolean的值,对于为true的元素它会包括在返回结果中,如果是false它就不会包括在结果中

var arr = [1,2,3,4,5,6]
var filteredArr=arr.filter(item=>item>4)
console.log(filteredArr)//[5,6]
# 15】数组测试

①检测所有元素是否满足一定条件,满足的话就返回true,否则就返回false

[].every()方法:内部传入回调函数,这个回调函数默认传入三个参数item,index,self

var arr=[1,2,3,4,5,6]
var res=arr.every(item=> item > 0)
console.log(res)//true
var res=arr.every(item=> item > 2)
console.log(res)//false

②如果有一个元素,满足条件就返回true,否则的话返回false

[].some()方法:内部传入回调函数,这个回调函数默认传入三个参数item,index,self

var arr=[1,2,3,4,5,6]
var resSome=arr.some(item=> item > 5)
console.log(resSome)//true
var resSome=arr.some(item=> item > 7)
console.log(resSome)//false
# 16】数组的解构操作符
var arr=[1,2,3]
var [a,b,c]=arr
console.log(a,b,c)//1 2 3
var [d,e]=arr
console.log(d,e)//1 2
var [,f]=arr
console.log(f)//2
# 17】数组rest操作符
var arr=[1,2,3,4,5,6,7,8]
var [a,b,...rest]=arr
console.log(a,b,rest)//1 2 [3,4,5,6,7,8]
var [a,,b,...rest]=arr
console.log(a,b,rest)//1 3 [4,5,6,7,8]

还可以用于函数参数,接收不固定个数的参数

通过rest传参合并成数组,通过spread操作符拆数组

function variousArgs(...args){//args本来是数组,[x1,x2,x3...]
    console.log(args)
}
variousArgs(1,2,3)//[1,2,3]
# 18】Spread操作符
//对于对象
var post={
    id:1,
    title:'标题1',
    content:'这是内容'
}
console.log(post)//{id:1,title:'标题1',content:'这是内容'}
var postClone={...post}//深克隆
console.log(postClone)//{id:1,title:'标题1',content:'这是内容'}
console.log(postClone===post)//false内存地址不一样
console.log(postClone==post)//true值相等

//对于数组
var arr=[1,2,3]
var arrClone=[...arr]
console.log(arrClone)//[1,2,3]
console.log(arrClone===arr)//false内存地址不同
console.log(arrClone==arr)//值相同
# 19】多维数组
var arr=[]
for(let i=0;i<5;i++){
    arr[i]=[]
    for(let j=0;j<4;j++){
        arr[i][j]=i+j
    }
}
console.log(arr)//五行四列的数组
//[0,1,2,3]
//[1,2,3,4]
//[2,3,4,5]
//[3,4,5,6]
//[4,5,6,7]

# 十七、对象

# 1】创建对象
var employee={
    name:'张三',
    age:20,
    position:'前端工程师',
    signIn:function(){
        console.log('张三打卡')
    }
}

var employee2=new Object()
employee2.name="李四";
employee2.signIn=function(){
    console.log("李四打卡")
}
# 2】对象属性

每个对象里的键值对都是对象的属性,它跟变量一样可以有任何值,比如是数字、字符串、数组、函数、另一个对象;如果键值对的值是函数的话,那么这个函数又可以叫做方法,也就是这个对象的行为,它能做一些事情。

var employee={
    name:'张三',
    age:20,
    position:'前端工程师',
    signIn:function(){//方法
        console.log('张三打卡')
    },
    "birth-date":"1999-05-25"
}
//对象属性的访问
console.log(employee.name)//张三
console.log(employee["name"])//张三
console.log(employee["birth-date"])//只有这种方式能访问,带特殊字符
# 3】遍历对象属性
# ①Object.keys(xxx)
var employee={
    name:'张三',
    age:20,
    position:'前端工程师',
    signIn:function(){//方法
        console.log('张三打卡')
    },
    "birth-date":"1999-05-25"
}
console.log(Object.keys(employee))//["name","age","position","signIn","birth-date"]
# ②for(key in obj)
for(key in employee){
    console.log(key)
    //name
    //age
    //position
    //signIn
    //birth-date
}
# 4】删除对象属性
var employee={
    name:'张三',
    age:20,
    position:'前端工程师',
    signIn:function(){//方法
        console.log('张三打卡')
    },
    "birth-date":"1999-05-25"
}
delete employee.name
console.log(employee)//name属性被删除
# 5】构造函数

使用构造函数是另一种创建对象的方式,与上面两种方式不同的是,使用构造函数我们可以预先定义好对象有哪些默认的属性,然后我们可以用new关键字来创建实例(根据构造函数创造出来的对象)

类比:工厂-构造函数;图纸-Prototype;汽车-实例

function Employee(name,position){//初始化属性
    this.name=name
    this.position=position
}
var emp1=new Employee('张三','前端工程师')
console.log(emp1)
var emp2=new Employee('李四','后端工程师')
console.log(emp2)
# 6】对象的this关键字

this关键字指代的是对象实例本身,可以用它来访问自身的属性(这里this.name来指代Employee所拥有的属性)

var emp3={
    name:'李四',
    position:'后端工程师',
    signIn(){
        console.log(this.name+'上班打卡')
    }
}
emp3.signIn()//李四上班打卡

emp3.goToWork=function (){
    console.log(this.name+'去上班')
}
emp3.goToWork()//李四去上班

emp3.goToWork=()=>{
    console.log(this.name+'去上班')
    console.log(this)
}
emp3.goToWork()
//去上班
//window对象
# 7】对象getters和setters

其中get和set是关键字,set函数只能接收一个参数,类似于vue的计算属性

//对象字面量
var person={
    firstName:'三',
    lastName:'张',
    get fullName(){
        return this.lastName + this.lastName
    },
    set fullName(fullName){
       let [lastName,firstName]=fullName.split(', ')
       this.lastName=lastName
       this.firstName=firstName
    }
}
console.log(person.fullName)//张三
person.fullName='李,四'
console.log(person.fullName)//李四
console.log(person.lastName,person.firstName)//李 四

构造函数创建对象时,需要时用**Object.defineProperty()**方法,这个方法里面需要传入三个参数。第一个参数是:要定义getter和setter的对象;第二个参数是:要定义的getter和setter这个属性的名字;第三个参数是:一个对象,具体的getter和setter函数

Object.defineProperty(obj, prop, descriptor)
//构造函数
function Employee2(name,position){
    this.name=name
    this.position=position
}
var emp1= new Employee2('赵六','前端工程师')
//应当直接在`Object`构造器对象上调用此方法,而不是在任意一个 `Object` 类型的实例上调用。
Object.defineProperty(emp1,"info",{
    get:function(){
        return this.name +' '+this.position
    },
    set:function(info){
        let [name,position]=info.split(' ')
        this.name=name
        this.position=position
    }
})
console.log(emp1.info)//赵六 前端工程师
emp1.info="赵七 后端工程师"
console.log(emp1.name,emp1.position)//赵七 后端工程师
# 8】原型属性

储存着共享的属性,还有一个构造函数的引用指向它的构造函数,还有原型链

//构造函数
function Employee(name,position){
    this.name=name
    this.position=position
    this.signIn=function(){
        console.log(this.name+'打卡')
    }
}
var emp1= new Employee('张三','前端工程师')
var emp2= new Employee('李四','后端工程师')
console.log(emp1)//Employee {name:'张三',...}
console.log(emp2)//Employee {name:'李四',...}

console.log(Employee.prototype)//{constructor:f}
Employee.prototype.age=20
console.log(emp1)//20
console.log(emp2)//20

Employee.prototype.printInfo=function(){
    console.log(this.name,this.age,this.position)
}
emp1.printInfo()//张三 20 前端工程师
emp2.printInfo()//李四 20 后端工程师

console.log(emp1.__proto__)//一些共享的原型属性
console.log(Employee.prototype)//一些共享的原型属性,和上面一样
console.log(emp1.__proto__===Employee.prototype)//true
console.log(Object.getPrototypeOf(emp2))//一些共享的原型属性,和上面一样
# 9】Object.create()
console.log(emp1)//打印实例
for(key in emp1){
    console.log(key)//打印实例的所有key值
}

var manager=Object.create(emp1)//创建一个相同实例,深拷贝
console.log(manager)//Employee {name:'张三',...}
for(key in manager){
    console.log(key)
}
console.log(Object.getOwnPropertyNames(manager))//["name","position"]打印只有自身用于的属性,不包括原型共享的属性
# 10】原型链

原型链是说每个对象的原型都还会有上层的一个原型,直到遇到null,这种链式继承下来的原型就构成了原型链,JavaScript最顶层的对象是Object,它的原型是Object.prototype,但是它的原型的原型就是null。

# 11】修改原型的指向

Object.setPrototypeOf(xxx,aaa)其中第一个参数是:需要改变原型的对象名称,第二个参数是:需要改变到的原型名称

# 12】call&apply&bind

用来改变函数中this的指向

var emp={
    id:1,
    name:'雷浩',
    printInfo(){
        console.log('员工姓名:'+ this.name)
    },
    department:{
        name:'技术部',
        printInfo(){
            console.log('部门名称:'+this.name)
        }
    }
}
//函数里的this指向的是函数'.'前面的对象
emp.printInfo()//员工姓名:雷浩
emp.department.printInfo()//部门名称:技术部

apply和bind都是在call方法上面有小改变;apply方法里面传参必须使用数组的方式;bind方法必须再次调用才能起效果

var emp1={
    id:1,
    name:'雷浩'
}
function printInfo(dep1,dep2,dep3){
    console.log('员工姓名:'+this.name,dep1,dep2,dep3)
}
printInfo()//员工姓名:

printInfo.call(emp1)//员工姓名:雷浩 undefined ...
printInfo.call(emp1,'技术部','IT事件部','总裁办公室')//员工姓名:雷浩 技术部 IT事件部 总裁办公室
printInfo.apply(emp1,['技术部','IT事件部','总裁办公室'])//员工姓名:雷浩 技术部 IT事件部 总裁办公室
printInfo.bind(emp1,'技术部','IT事件部','总裁办公室')//不能打印出来,因为结果是被返回了,需要再次调用
var empPrintInfo=printInfo.bind(emp1,'技术部','IT事件部','总裁办公室')
empPrintInfo()////员工姓名:雷浩 技术部 IT事件部 总裁办公室

# 十八、面向对象

# 1】定义class

ES6规范中才有了面向对象语法,只不过它是prototype原型的语法糖,也就是说它的内部还是会转换成Object和prototype这种形式的,它的面对对象语法只是方便大家编写面向对象的代码。

比如下面代码就和用之前学习的构造函数创建对象是一样的。

class Employee{
    constructor(name,position){//定义构造方法,名字固定
        this.name=name
        this.position=position
    }
}
var emp=new Employee('雷浩','前端工程师')
console.log(emp)//Employee {name:'雷浩',position:'前端工程师'}
# 2】成员方法

在类里面定义方法,直接写函数名(不需要使用function这个关键字),剩下的和定义普通函数一样,另外它也可以用this来引用自身的属性。

class Employee{
    constructor(name,position){//定义构造方法,名字固定
        this.name=name
        this.position=position
    }
    
    signIn(){
        console.log(this.name+'打卡上班')
    }
    
    get info(){
        return this.name +' '+ this.position
    }
    set info(info){
        let[name,position]=info.split(' ')
        this.name=name
        this.position=position
    }
}
var emp1=new Employee('雷浩','前端工程师')
emp1.signIn()//雷浩打卡上班
console.log(emp1.info)//雷浩 前端工程师
emp1.info="李四 后端"
console.log(emp1.info)//李四 后端
console.log(emp1.name,emp1.positon)//李四 后端
# 3】实现继承

实现面向对象里面的继承,使用extends关键字

一个子类只能继承一个父类

class Employee{
    constructor(name,position){//定义构造方法,名字固定
        this.name=name
        this.position=position
    }
    
    signIn(){
        console.log(this.name+'打卡上班')
    }
    
    get info(){
        return this.name +' '+ this.position
    }
    set info(info){
        let[name,position]=info.split(' ')
        this.name=name
        this.position=position
    }
}

//一个子类只能继承一个父类
class Manager extends Employee{
    constructor(name,position,dept){//重写构造函数
        super(name,position)//调用父类的构造函数
            this.dept=dept//子类自定义新增属性
    }
}
var manager=new Manager('王五','经理','技术部')
console.log(manager)//Manager {name:'王五',position:'经理',dept:'技术部'}
manager.signIn()//王五打卡上班

最简单的继承方式,是通过call方法来修改this指向,将父类的构造方法绑定在子类上

function Animal(){//父类构造方法
    this.species='动物'
}
function Cat(){//子类构造方法
    Animal.call(this)//子类中修改this指向,将父类构造函数绑定到子类上
    this.name='小黄'
}
var cat1=new Cat()
console.log(cat1.name)//小黄
//此时就可以直接通过子类访问父类中的属性
console.log(cat1.species)//动物
# 4】成员变量(新)

成员变量是指在类里面定义变量

成员变量等同于就是在构造方法中设置属性

class Employee{
    dept=''
}
var emp=new Employee()
console.log(emp)//Employee {dept:''}
emp.dept='技术部'
console.log(emp)//Employee {dept:'技术部'}
# 5】静态成员(新)

静态成员变量和方法是class级别的,它只能通过类来访问

class Page{
    static count=0;
    static increseViewCount(){
        Page.count++;
    }
}
Page.count++//静态属性只能通过类名来访问,实例化后是不能访问的
console.log(Page.count++)//1
Page.increseViewCount()
console.log(Page.count)//2

# 十九、字符串

# 1】字符串定义与转义
//创建字符串
var str ="hello"
console.log(str)//hello

//使用对象创建字符串
var str2=new String('你好')
console.log(str2)//String {"你好"}(这个对象有两个元素,0:"你",1:"好")


//转义字符
str = "他说:\"我们出去玩吧!\""
console.log(str)//他说:"我们出去玩吧!"
//也可以直接双引号外面使用单引号 或者 单引号外面使用双引号
str = '他说:"我们出去玩吧!"'
console.log(str)//他说:"我们出去玩吧!"
//也可以使用Unicode字符转义
console.log("\u1010")//တ
console.log("\u2233")//∳
# 2】字符串遍历
var str='很好'
for(let i=0;i<str.length;i++){
    console.log(str.charAt(i))//"很" "好"
}
for(let c of str){
    console.log(c)//和上面一样的结果
}
# 3】字符串裁切
var str="This....."
//slice在数组中也讲到过
console.log(str.slice(0,3))//Thi(包括前面的index=0不包括后面的index=3)
console.log(str.slice(3))//s.....
console.log(str.slice(0,-1))//This....(-1就是指最后一位)
console.log(str.slice(4,1))//返回空字符串(如果开始大于结束的索引就是返回空字符串)
console.log(str.slice(-6,-1))//s....(倒数6至倒数1不包括后面的倒数1)

//使用字符串封装的substring
console.log(str.substring(0,3))//Thi(和slice一样)
console.log(str.substring(3))//s.....(和slice一样)
console.log(str.substring(0,-1))//返回空字符串(substring方法index=负数的话会把index转成0,这里就相当于(0,0);)
console.log(str.substring(4,1))//his(substring方法只要是前面大于后面就会把前后交换一下,此时就是(1,4);)
console.log(str.substring(-6,-1))//放回空字符串(和上面的(0,-1)同理,变成了(0,0))
# 4】字符串拼接
var str1="hello"
var str2="world"
console.log(str1+str2)//helloworld
console.log(str1.concat(str2))//helloworld
# 5】大小写转换
var str1="hello"
console.log(str1.toUpperCase())//HELLO
console.log("WORLD".toLocaleLowerCase())//world
# 6】去除空格
//去掉字符串首尾空格
var str="    hello world   "
console.log(str.trim())//hello world
# 7】模板字符串
var longStr = ` sdasd sd a`//这是模板字符串,打印会样式不变的打印出来

# 二十、正则表达式

是一种表达字符串的规则或者模式的,咱们可以利用它来判断一个字符串是否符合某种规律或者规则,它的用途很广泛,比如验证邮箱,验证手机号有没有特殊字符等等。

# 1】创建正则表达式
var str = "where when what" 
//方式一:利用字面值
var re = /wh/
//方式二:RegExp创建一个正则表达式对象
var re2 = new RegExp("wh")

//两种方式执行正则表达式
//1.exec()的方式
//第一次使用exec()就匹配第一次遇到的字符输出index
console.log(re.exec(str))//["wh",index:0,input:"where when what",groups:undefined]
//第二次使用exec()就匹配第二次遇到的字符输出index
console.log(re.exec(str))//["wh",index:6,input:"where when what",groups:undefined]
//第三次使用exec()就匹配第三次遇到的字符输出index
console.log(re.exec(str))//["wh",index:11,input:"where when what",groups:undefined]
//如果没有了匹配项就直接输出null
console.log(re.exec(str))//null

//2.test()的方式
console.log(re.test(str))//true包含'wh'
# 2】单纯字符匹配
var  str=`This str contains 123 
CAPITALIZED letters and _-^% symbols`
console.log(/T/.test(str))//true
console.log(/This/.test(str))//true
console.log(/Thiss/.test(str))//false
console.log(/12/.test(str))//true
console.log(/1234/.test(str))//false
console.log(/_-^/.test(str))//true
# 3】特殊字符匹配
var str=`This Thas str contains 123 
CAPITALIZED letters and _-^% symbols`
//	.表示任意字符,g表示全局匹配
console.log(str.match(/TH.s/g))//["This","Thas"]
console.log(str.match(/1.3/g))//["123"]
//	\d表示0-9的数字。\D查找非数字字符。
console.log(str.match(/\d/g))//["1","2","3"]
//	\w表示a-z、A-Z、0-9,以及下划线, 包含 _ (下划线) 字符。	\W表示查找非上述的字符。
console.log(str.match(/\w/g))//["T", "h", "i", "s", "T", "h", "a", "s", "s", "t", "r", "c", "o", "n", "t", "a", "i", "n", "s", "1", "2", "3", "C", "A", "P", "I", "T", "A", "L", "I", "Z", "E", "D", "l", "e", "t", "t", "e", "r", "s", "a", "n", "d", "_", "s", "y", "m", "b", "o", "l", "s"]
//	\s表示查找空白字符。\S表示查找非空白字符。
console.log(str.match(/\s/g))//[" ", " ", " ", " ", " ", "↵", " ", " ", " ", " "]

如果想要匹配中文,就需要调用Unicode码

console.log("你好".match(/\u4f60/g))//["你"]
# 4】匹配次数
var str=`This Thas str contains 123 
CAPITALIZED letters and _-^% symbols`
//将匹配到"This"之后的任意字符都进行匹配,直到遇到\(这里只匹配到123是因为有换行符\n)
console.log(str.match(/This.*/g))//["This Thas str contains 123 "]
//匹配含义多个相同字符的字符串(区分大小写)
console.log(str.match(/t+/g))//["t", "t", "tt"]
console.log(str.match(/T+/g))//["T", "T", "T"]
//?可以匹配一个字符出现了0次或者1次
console.log(str.match(/x?/g))//["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""]
console.log(str.match(/t?/g))//["", "", "", "", "", "", "", "", "", "", "", "t", "", "", "", "", "", "t", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "t", "t", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""]
//{}指定匹配精确的出现了几次的结果
console.log(str.match(/t{2}/g))//["tt"]
//匹配{n,m}出现n到m次的结果
console.log(str.match(/\d{1,3}/g))//["123"]
console.log(str.match(/\d{1,2}/g))//["12", "3"]
//代表至少出现1次且是数字型的字符串
console.log(str.match(/\d{1,}/g))//["123"]
# 5】区间、逻辑和界定符
# 方括号(区间)

方括号用于查找某个范围内的字符:

注意区别:方括号中使用^是非的意思,正则表达式//之间使用^就是指以这个字符开始匹配,使用$是结束匹配。

[abc]//查找方括号之间的任何字符。
[^abc]//查找任何不在方括号之间的字符。
[0-9]//查找任何从 0 至 9 的数字。
[a-z]//查找任何从小写 a 到小写 z 的字符。
[A-Z]//查找任何从大写 A 到大写 Z 的字符。
[A-z]//查找任何从大写 A 到小写 z 的字符。
[adgk]//查找给定集合内的任何字符。
[^adgk]//查找给定集合外的任何字符。
(red|blue|green)//查找任何指定的选项。
# 或的逻辑
console.log(str.match(/This|contains/g))//["This", "contains"]
# 界定符
var str="this athata this and that"
//^加在前面就只返回第一次出现的字符串(^界定开头的字符)
console.log(str.match(/^this/g))//["this"]
console.log(str.match(/this/g))//["this", "this"]
//$加在后面就只返回最后一次出现的字符串($界定结尾的字符)
console.log(str.match(/that$/g))//["that"]
console.log(str.match(/that/g))//["that", "that"]
//\bxxxxx\b字符串前后包裹\b(只匹配该字符串,而"athata"就没有被匹配到)
console.log(str.match(/\bthat\b/g))//["that"]
# 修饰符

修饰符用于执行区分大小写和全局匹配:

i//执行对大小写不敏感的匹配。
g//执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)。
m//执行多行匹配。
# 6】分组

正则表达式可以使用小括号对模式进行分组,分组的内容可以当成一个整体,并且执行结果会把分组的内容返回回来

var str = `this that this and that`
//现在想匹配第一个this的th和最后一个that的th
console.log(/(th).*(th)/.exec(str))//["this that this and th", "th", "th", index: 0, input: "this that this and that", groups: undefined]

var str=`aaaab abb cddaa`
//只想匹配aaaa不想要aa的话,可以先这样分组在限制出现的次数
console.log(str.match(/(aa){2}/g))//["aaaa"]
# 7】常见正则表达式
# ①验证手机号

用1开头,第二个数字是3~9,剩下的是任意数字

var mobileRe=/^1[3-9]\d{9}$/g
console.log(mobileRe.test(13846546548))//true
console.log(mobileRe.test(10846546548))//false
console.log(mobileRe.test(1584654654118))//false长度限制,因为加了$结束符号
# ②验证邮箱

@前面只能是数字、大小写字母、下划线和中划线,@后面必须是.com/cn

var emailRe=/^([A-Za-z0-9_\-\.]+)@([A-Za-z0-9_\-\.]+)\.([A-Za-z]){2,5}$/g
console.log(emailRe.test("456a_46@1484.com"))//true
console.log(emailRe.test("456a_46@qqcom"))//false
console.log(emailRe.test("456a_46@qq.commmm"))//false
# ③验证用户名

6-15个字符,以字母开头,只允许大小写字母、数字和下划线

var usernameRe=/^[A-Za-z][A-Za-z0-9_]{5,14}$/g
console.log(usernameRe.test("abc"))//false
console.log(usernameRe.test("abc_123"))//true
console.log(usernameRe.test("_abc_123"))//false
# 8】字符串替换replace()

可以使用string对象里面的replace方法

var str="Tish is an apple"
console.log(str.replace("Tish","This"))//"This is an apple"

var str2="Tish 1is 2an 3apple"
//想要把所有的数组去掉
console.log(str2.replace(/\d+/g,""))//把数字替换成空字符串
//打印结果"Tish is an apple"

使用正则匹配html标签,然后直接过去标签内的值

var html=`<span>hello</span><div> world</div>`
console.log(html.replace(/<[^>]*>([^<>]*)<\/[^>]*>/g,"$1"))//hello world
//$1、$2、...、$99	与 regexp 中的第 1 到第 99 个子表达式相匹配的文本。
//这里replace函数参数一中只有一个正则表达式,所以就使用$1就可以获取到匹配到的值了

具体$字符具有特定的含义参见w3chool

#repalce定义

用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。

stringObject.replace(regexp/substr,replacement)//参数一:必需,规定的字符串或者正则表达式
//参数二:必需,一个字符串值。规定了替换文本或生成替换文本的函数。
//!!!replacement 中的 $ 字符具有特定的含义。详情见W3C
# ②有$1,$2...的例子
//把 "Doe, John" 转换为 "John Doe" 的形式:
var str = "Doe, John";
str.replace(/(\w+)\s*, \s*(\w+)/, "$2 $1");
//说明:$1,$2上就是按顺序对应小括号里面的小正则,捕获到的内容。
# 9】字符串分隔split()

它可以用一些特定的字符,对字符串进行分隔,然后返回分隔后的数组。

var tags="html,css,javascript"
console.log(tags.split(","))//以数组形式返回结果["html","css","javascript"]

它也接受一个正则表达式作为一个参数(比如说需要分隔的字符串,按一定的规律进行分隔)

var str="This | is , an & apple"
console.log(str.split(/\W+/g))//["This","is","an","apple"]
//\w是查找非单词字符。及非大小写字母以及_
//然后在分隔

# 二十一、JS的基本DOM操作

# 查找

(就是获取元素)

# (1),标准DOM API
document.getElementById(id);
 
document.getElementsByTagName(name);
 
document.getElementsByName(name);
 
document.getElementsByClassName(names);
 
document.querySelectorAll(selectors);
# (2),亲属访问
parentNode     /*访问父节点*/
parentElement  /*父元素*/
 
childNodes  /*访问子节点*/
children    /*子元素*/

//下面获取操作有IE8兼容问题

firstChild  /*访问第一个子节点*///-------------IE8中获取到的是元素
firstElementChild    /*第一个子元素*///-------------IE8不支持
 
lastChild 或 childNodes[childNodes.length - 1]   /*访问最后一个子节点*///-------------IE8中获取到的是元素
lastElementChild    /*最后一个子元素*///-------------IE8不支持
 
previousSibling     /*访问上一个兄弟节点*///-------------IE8中获取到的是元素
previousElementSibling    /*上一个兄弟元素*///-------------IE8不支持
 
nextSibling     /*访问下一个兄弟节点*///-------------IE8中获取到的是元素
nextElementSibling     /*下一个兄弟元素*///-------------IE8不支持
 
attributes[0]     /*访问第一个属性节点*/
# (3),属性获取
getAttribute(attributeName);
 
getAttributeNode(attrName);
# 增加
# (1),创建
document.createElement(tagName);    /*元素节点*/
document.createTextNode(data);    /*文本节点*/
document.createAttribute(name);    /*属性节点*/
cloneNode(deep)
# (2),加入
document.write(markup) /*一般不用,文档加载完毕后使用会覆盖页面!*/
 
appendChild(aChild)    /*追加到结尾处*/
 
innerHTML
 
innerText
 
insertBefore(newElement, referenceElement)    
/*用法----父元素.insertBefore(新元素, 旧元素)*/
# (3),其它
style. //的操作
 
setAttribute(name, value)
# 删除
# 删除元素
removeChild(child)
 
removeAttributeNode(attributeNode)
# 修改
# (1),修改节点
//删除节点再加入

replaceChild(newChild, oldChild)
/*用法----父元素.replaceChild(新元素, 旧元素)*/
# (2),修改样式
style.xxx = sss ;
 
setAttribute(name, value)
# (3),修改文本
innerHTML
innerText

nodeValue
/*节点操作(删除旧文本节点再加入新文本节点)*/
# (4),修改属性
setAttribute(name, value)
 
// . 属性名 = 值 
Last Updated: 8/15/2020, 1:39:46 PM