JavaScript基础(基于黑马pink)

一.JavaScript语法阶段

1.引言

JavaScript是一种运行在客户端的脚本语言
脚本语言:不需要编译,运行过程中有js解释器(js引擎)逐行来进行解释并执行
现在也可以基于Node.js技术进行服务器端编程

作用:
(1)表单动态检验(密码强度检测)
(2)网页特效
(3)服务端开发(Node.js)
(4)桌面程序(Electron)
(5)App
(6)物联网
(7)游戏开发(cocos2d-js)

浏览器分为渲染引擎JS引擎两部分
渲染引擎:用来解析HTML和CSS,俗称内核
JS引擎:用来读取网页中的JavaScript代码,对其处理后运行,比如chrome的V8。JS引擎执行JS语句是一行一行的执行的。

组成: ECMAScript(JavaScript语法)、DOM(页面文档对象模型)、BOM(浏览器对象模型)

JS三种形式:

1.行内式:
<input type="button" value="点我试试" onclick="alert('Hello world')" />
2.内嵌JS:
<script>
	alert("Hello world!");
</script>
3.外部JS文件
<script src="XXXX"></script>
引用外部JS文件的script标签中间不可以写代码
使用JS代码量比较大的情况

使用!+Tab键可以快速生成html文件的基本内容
JS注释
//  1.单行注释 ctrl+/

/* 2.多行注释 默认快捷键 shift+alt+a
	2.多行注释 vscode中修改多行注释的快捷键: ctrl+shift+/
*/
JavaScript输入输出语句
<script>
        //浏览器弹出警示框
        alert("AAA");
        //浏览器控制台打印输出信息
        console.log("BBB");
        //浏览器弹出输入框,提供给用户输入,取过来的值是字符型的
        prompt("请输入你的密码:");
    </script>

2.变量

2.1变量初始化

<script>
		//变量声明+赋值=变量初始化
        var age = 10;      
        //多个变量用逗号隔开
        var age = 13,gender = '男';
        //只声明不赋值的结果是undifined
</script>

2.2数据类型

JS是一种弱类型或者说动态语言
js的变量数据类型是只有程序在运行过程中,根据等号右边的值来确定的
JS的简单数据类型有:
Number :整数、浮点数、0b10001 034 34 0x34
Number.MAX_VALUE(最大值) Number.MIN_VALUE(最小值)
Infinity(无穷大)、-Infinity(无穷小)
isNaN() 判断非数字,是数字返回false,不是数字返回true
String :推荐使用单引号,外双内单或者外单内双
var str = ‘Hello World!’;
console.log(str.length);
字符串相加与java类似
console.log(“hello world”+“!”);
字符串的不可变:指的是里面的值不可变,虽然看上去可以改变内容,但其实是地址变了,内存中开辟了一个内存空间
Boolean(false、true):默认是false
Undefined Null
获取变量数据类型:

var gender = '男';
alert(typeof gender);

数据类型转化:
1.转换为string
var num = 10;
alert(num.toString());
alert(String(num));
//隐式转换
alert(num+‘’);
2.string转换为number
var str = ‘23’;
alert(parseInt(str));
parseFloat
3.转换为布尔型
Boolean(var);

复杂数据类型有:Object

解释型语言和编译型语言
编译器是在代码执行之前进行编译,生成中间代码文件
解释器是在运行时进行及时解释,并立即执行

3.运算符

一共有算术运算符递增和递减运算符比较运算符逻辑运算符赋值运算符

alert(3/2);  //结果为1.5,而不是1
浮点依旧还是有误差
===、!== 要求值和数据类型都一致
短路运算和其他语言一样

4.流程控制

顺序结构分支结构循环结构
if-else if-else语句、三元表达式、switch-case语句与java类似
case里面匹配时是===全等
断点调试:
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/2cfdde050f374615bdfb002f30258985.png
for循环、while循环等与java也类似

5.数组

//js里的数组元素可以是不同数据类型的
		var num = [];
        num[1] = 23;
        num[2] = 'sdfds';
        for(var i=0;i<num.length;i++){
            console.log(num[i]);
        }
//js里的数组长度可以进行修改
		1.新增数组元素,修改length长度
        var num = [];
        num[1] = 23;
        num[2] = 'sdfds';
        console.log(num.length);
        num.length = 100;
        2.新增数组元素,修改索引号,追加数组元素
        var color  = ['red','green','blue'];
        arr[3] = 'pink';

6.函数

函数封装了一些被大量重复使用的代码

		function getSum(num1,num2){
            var sum = 0;
            for(var i=num1;i<=num2;i++){
                sum += i;
            }
            return sum;
        }
        var num = getSum(1,100);
//函数没有return时则返回undefined

arguments:

function fn(){
	console.log(arguments);  //里面存储了所有传递过来的实参
}
fn(1,2,3);
//arguments是一个伪数组,并不是真正意义上的数组
//1.具有数组的length属性
//2.按照索引的方式进行存储的
//3.它没有真正数组的一些方法,如pop()  push()等

函数声明的两种方式:

//1.利用函数关键字自定义函数
function fn(){
	console.log(arguments); 
}
fn(1,2,3);
//2.函数表达式(匿名函数)
var 变量名 = function(){};
var fun = function(){
	console.log('我是函数表达式'):
}
fun();

7.作用域

全局变量:在全局作用域下的变量、或者在函数内部没有声明直接赋值的变量
执行效率来看全局变量和局部变量:
1.全局变量只有浏览器关闭的时候才会销毁,比较占内存资源
2.局部变量当我们程序执行完毕就会销毁,比较节约内存资源
JS在es6的时候才新增了块级作用域,也就是说在之前下列代码是合理的:

if(3<5){
	var num = 3;
}
console.log(num);

作用域链: 内部函数访问外部函数的变量,采取的是链式查找的方式来决定取哪个值,这种结构我们称为作用域链就近原则

js中的var与c++中的var区别(个人理解)

(1)如果都是函数内部定义的局部变量,自然条件下外部都不能访问
(2)外部定义了全局变量,js函数内不需要通过形参传递就可以使用,而c++需要传递进来才能使用

window是一个全局对象,定义在全局作用域中的变量、函数都会变成window对象的属性和方法,这也使得js函数内可以不经参数传递就直接使用全局变量

8.预解析

1.js引擎运行js分为两步: 预解析 代码执行
(1)预解析 js引擎会把js里面所有的var还有function提升到当前作用域的最前面
(2)代码执行 按照代码书写的顺序从上往下执行
2.预解析分为变量预解析(变量提升)和函数预解析(函数提升)
(1)变量提升就是把所有的变量声明提升到当前的作用域最前面,不提升赋值操作
(2)函数提升就是把所有的函数声明提升到当前作用域的最前面,不调用函数

console.log(num);
var num = 10;
//这两句代码在预解析时相当于下述代码:
var num;   //注意:变量提升只针对声明而不针对赋值
console.log(num);
var num = 10;
//所以最后的结果是undifined
//函数声明和函数表达式的执行顺序不同,函数声明会被提升,函数表达式不会提升
//函数表达式不会被提升,而是在代码执行到它们所在的行时进行赋值
fun();
var fun = function(){
		console.log(22);
}
预解析相当于:
var fun;
fun();        //这行报错
var fun = function(){
		console.log(22);
}
//JavaScript 引擎会先将函数声明提升到代码的顶部,然后再执行函数调用
fn();
function fn(){
	console.log(11);
}

//这相当于执行:
function fn(){
	console.log(11);
}
fn();

案例1

var num = 10;
fun();
function fun(){
	console.log(num);
	var num = 20;
}
相当于执行以下操作:
var num;
// function里面的也要进行变量提升
function fun(){
	var num;     //注意这两处
	console.log(num);
	num = 20;    //注意这两处
}
num = 10;
fun();
//所以最终结果为undifined

案例2

f1();
console.log(c);    
console.log(b);	
console.log(a);       //这行报错,其余都为9
function f1(){
	var a=b=c=9;          //这相当于var a=9;b=9;c=9; 所以b和c是全局变量
	console.log(a);		//集体声明应该为   var a=9,b=9,c=9;
	console.log(b);
	console.log(c);
}

9.对象

创建对象的第一种方式

//1.利用对象字面量创建对象
var obj = {
            name: '张三丰',
            age: 18,
            sex: '男',
            sayHi: function(){
                console.log('Hello World!');
            }
            }
        //(1)里面的属性或者方法我们采用键值对的形式
        //(2)多个属性和方法之间使用逗号隔开
        //(3)方法冒号后面跟的是一个匿名函数
    
2.使用对象
//(1)使用对象名.属性名的方式调用对象的属性
//(2)使用对象名['方法名']的方式调用对象的属性
//(3)使用对象名.方法名调用对象的方法

创建对象的第二种方式

//使用new Object创建对象
var obj = new Object();
        obj.name = '张三丰';
        obj.age = 18;
        obj.sex = '男';
        obj.sayHi = function(){
            console.log('hi~');
        }

创建对象的第三种方式

//使用构造函数创建对象
//前两种创建对象的方式一次只能创建一个对象
//构造函数的语法格式
function 构造函数名(){
	this.属性 = 值;
	this.方法 = function(){}
}
new 构造函数名();

//构造函数内部一定要使用this
function Star(name,age,sex){
            this.name= name;
            this.age = age;
            this.sex = sex;
            this.sing = function(sang){
                console.log(sang);
            }
        }
        var ldh = new Star('刘德华',18,'男');
        ldh.sing('冰雨');

//new关键字的执行过程:
(1)new构造函数可以在内存中创建一个空的对象
(2)this就会指向刚才创建的空对象
(3)执行构造函数里面的代码,给这个空对象添加属性和方法
(4)返回这个对象

//遍历对象
//对于ldh对象来说:
for(var k in ldh){
	console.log(k);   //得到的是属性名
	console.log(ldh[k]);  //得到的是属性值
}

10.内置对象

js中的对象分为三种:自定义对象、内置对象、浏览器对象
内置对象是js自带的一些对象,最大的优点是帮助我们开发
例如:Math、Date、Array、String等;
MDN文档官网

10.1 Math对象

//绝对值
Math.abs(-1);
//上取整
Math.ceil(1.1);
//下取整
Math.floor(1.1);
//四舍五入   .5会往大了取
Math.round(1.4);
//随机数方法Math.random()返回[0,1)之间的一个浮点数
//获得[min,max)之间的随机整数
Math.floor(Math.random()*(max-min))+min
//获得[min,max]之间的随机整数
Math.floor(Math.random()*(max-min+1))+min

10.2 Date对象

//没有参数返回当前系统的当前时间
        var date = new Date();
        console.log(date);
        var date1 = new Date('2024-3-8 12:33:12');
        console.log(date1);
//按照需要的格式输出当前日期
		var date = new Date();
        var year = date.getFullYear();
        var month = date.getMonth()+1;    //这里需要+1
        var dates = date.getDate();
        var day = date.getDay();
        var arr = ['星期日','星期一','星期二','星期三','星期四','星期五','星期六']
        var hour = date.getHours();
        var minute = date.getMinutes();
        var second = date.getSeconds();
        console.log('今天是'+year+'年'+month+'月'+dates+'日'+arr[day]+hour+':'+minute+':'+second);

10.3 数组对象

//数组创建的两种方式
//1.用字面量来创建
var arr = [1,2,3];
//2.用new Array()
var arr1 = new Array();
var arr2 = new Array(2);
var arr3 = new Array(2,3);

//检测是否为数组
//第一种:使用instanceof
var arr = [];
console.log(arr instanceof Array);
//第二种:使用isArray
Array.isArray(arr);

//数组元素的添加和删除
	var arr = [1,2,3];
        arr.push(4);       	//返回的是数组长度
        arr.unshift(5);		 //在开头添加
        console.log(arr);
        arr.pop();             //返回的是删除的元素的值
        arr.shift();       		//在开头删除
        console.log(arr);

//数组排序
sort函数接受一个可选的比较函数作为参数,当比较函数返回一个负值时,表示第一个参数应该排在第二个参数之前;当返回一个正值时,表示第一个参数应该排在第二个参数之后;当返回0时,表示两个参数相等,顺序不变
//降序
		var arr = [2,43,3,2,23,5];
        arr.sort(function(a,b){
            return b-a;
        });
        console.log(arr); 
//升序
		var arr = [2,43,3,2,23,5];
        arr.sort(function(a,b){
            return a-b;
        });
        console.log(arr);
//返回数组元素的下标,不存在则返回-1
		var arr = [23,3,5];
        console.log(arr.indexOf(3));
//数组转换为字符串
		//使用toString
		var arr = [23,4,34];
		console.log(arr.toString());
		//使用join,join里面为分隔符
		console.log(arr.join('-'));
		console.log(arr.join(''));

10.4字符串对象

//1.替换字符 replace('被替换字符','替换字符')
		var str = 'anady';
		//replace只替换第一个
		console.log(str.replace('a','b'));
		//replaceAll替换全部的
		console.log(str.replaceAll('a','b'));

//2.字符串转化为数组
		var str = 'red,pink,black';
		var arr = str.split(',');
		console.log(arr);	

11.数据类型

简单数据类型传参:

当把一个值类型变量作为参数传递给函数的形参时,其实是把变量在栈空间里的值复制了一份给形参,方法中修改不会影响到外部变量。

复杂数据类型传参:

在 JavaScript 中,复杂数据类型(例如对象和数组)作为参数传递给函数时,实际上传递的是引用而不是值的副本。这意味着,如果在函数内部修改了传递的对象或数组,那么外部的原始对象或数组也会受到影响。
但是需要注意的是
(1)你可以在函数里面修改参数对象的属性(在外部也生效),但是不可以直接对这个参数对象整体赋值(或者说在函数内部将参数重新赋值为一个新的对象或数组),这种整体赋值的操作在外部是不会生效的。

<script>
        function modifyObject(obj) {
            obj.key = 'modified';
        }

        let myObject = {
            key: 'original'
        };
        modifyObject(myObject);
        console.log(myObject); // 输出:{ key: 'modified' }
    </script>

(2)如果传递参数的属性也是一个对象,则可以整体修改这个属性对象,外部也会生效。

<script>
        function modifyNestedObject(obj) {
            obj.innerObject = {
                key: 'new value'
            };
        }

        let myObject = {
            innerObject: {
                key: 'original value'
            }
        };
        modifyNestedObject(myObject);
        console.log(myObject); // 输出:{ innerObject: { key: 'new value' } }
    </script>

二.Web APIs

Web APIs中主要学习DOM和BOM,是JS独有的部分
Web APIs是浏览器提供的一套操作浏览器功能页面元素的API(BOM和DOM)
MDN详细API

1.DOM

DOM(Document Object Model)文档对象模型,是W3C组织推荐的处理可扩展标记语言(HTML或者XML)的标准编程接口。
通过这些DOM接口可以改变网页的内容、结构和样式
在这里插入图片描述

1.1获取元素

1.1.1第一种方式:getElementById();
    //自上到下加载,script写到标签下面
	<div id="time">2024-3-8</divid>
    <script>
        var timer = document.getElementById('time');
        console.dir(timer);
    </script>
1.1.2 第二种方式:getElementsByTayName();

可以获取某个父元素内部所有指定标签名的子元素。注意:父元素必须是单个对象(必须指明是哪一个元素对象)

	<ul>
        <li>A</li>
        <li>B</li>
        <li>C</li>
        <li>D</li>
    </ul>
    <ol id = 'ol'>
        <li>AAA</li>
        <li>BBB</li>
        <li>CCC</li>
        <li>DDD</li>
    </ol>
    //第一种写法
    <script>
        var ol_tag = document.getElementsByTagName('ol');
        //返回的是获取过来元素对象的集合,以伪数组的形式存储
        var ol_li = ol_tag[0].getElementsByTagName('li');        //注意:这里要用ol_tag[0],不能直接用ol_tag
    </script>
    //第二种写法
    <script>
        var ol_id = document.getElementById('ol');
        //返回的是获取过来元素对象的集合,以伪数组的形式存储
        var ol_li = ol_id.getElementsByTagName('li');      
    </script>
    
1.1.3 第三种方式:HTML5新增方式
<div class="box">盒子</div>
    <div class="box">盒子</div>
    <div id="nav">
        <ul>
            <li>首页</li>
        </ul>
    </div>
    <script>
        //1.根据类名获得某些元素的集合
        var boxs = document.getElementsByClassName('box');
        console.log(boxs);

        //2.querySelector 返回指定选择器的第一个元素对象
        //querySelector里面的选择器需要加符号  .box  #nav
        var firstBox = document.querySelector('.box');
        console.log(firstBox);
        var firstNav = document.querySelector('#nav');
        console.log(firstNav);
        var li = document.querySelector('li');
        console.log(li);
        
       //3.querySelectorAll返回指定选择器的所有元素对象集合
        var allBox = document.querySelectorAll('.box');
    </script>
1.1.4第四种方式:获取特殊元素

获取body、html对象

<script>
        //1.获取body元素
        var body = document.body;
        //2.获取html元素
        var htmlEle = document.documentElement;      
</script>

1.2.事件基础

JS使我们有能力创建动态界面,而事件是可以被js侦测到的行为
简单理解:触发—响应机制

1.2.1事件三要素

事件由事件源事件类型事件处理程序三部分组成
(1)事件源:事件被触发的对象 btn

	<button id="btn">hk</button>
    <script>
        var btn = document.getElementById('btn');
    </script>

(2)事件类型:如何触发 什么事件 比如鼠标点击还是鼠标经过、键盘按下等
使用onclick和addEventListener()的最主要的区别在于onclick绑定了一个事件就不能再绑定,而addEventListener()可以绑定多个
(3)事件处理程序:通过一个函数赋值的方式完成

<button id="btn">hk</button>
    <script>
        var btn = document.getElementById('btn');
        btn.onclick = function(){      //onclick就是时间类型
            alert('哈哈');
        }
    </script>
1.2.2执行事件的步骤

(1)获取事件源
(2)绑定事件(也叫注册事件)
(3)添加事件处理程序

1.2.3操作元素
1.2.3.1.修改元素内容:innerText和innerHTML
<button id="btn">点击显示当前时间</button>
    <div>某个时间</div>
    <script>
        var btn = document.getElementById('btn');
        var div = document.querySelector('div');
        btn.onclick = function(){
            div.innerText = getDate();
        }
        function getDate(){
            var date = new Date();
            var year = date.getFullYear();
            var month = date.getMonth()+1;    //这里需要+1
            var dates = date.getDate();
            var day = date.getDay();
            var arr = ['星期日','星期一','星期二','星期三','星期四','星期五','星期六']
            var hour = date.getHours();
            var minute = date.getMinutes();
            var second = date.getSeconds();
            return '今天是'+year+'年'+month+'月'+dates+'日'+arr[day]+" "+hour+':'+minute+':'+second;
        }
    </script>

innerText和innerHTML的区别

//innerText不识别html标签,innerHTML识别,W3C建议使用后者
//innerText去除空格和换行,innerHTML保留空格和换行
1.2.3.2.修改常见元素属性

以src为例,href等其他属性同理

<img src = "XXXX" alt="" title="XX">
    <script>
        var img = document.querySelector('img');
        img.src = "YYYY";
        img.title = "YY";
    </script>
1.2.3.3.修改表单元素属性
//表单里的值是通过value得到的
<button>按钮</button>
<input type="text" value="输入内容">
    <script>
        var btn = document.querySelector('button');
        var input = document.querySelector('input');
        btn.onclick = function(){
            input.value = '被点击了';
            //如果想实现按钮点击后被禁用
            btn.disabled = true;
           	//this.disabled = true;  这行会更好
        }
    </script>
1.2.3.4样式属性操作
1.2.3.4.1通过行内样式操作进行样式属性修改

(1)获取对象
(2)使用 对象.style.属性=XXX;
注意: 1.js里面的样式采取驼峰命名法,比如fontSize
2.js修改style样式操作,产生的是行内样式,CSS权重比较高
3.这种方式适合要修改的样式比较少,或者功能简单的情况

//onfocus和onblur案例
<input type="text" value="手机">
    <script>
        var input = document.querySelector('input');
        input.style.color = "#999";
        input.onfocus = function(){
            if(this.value == '手机'){
                this.value = null;
            }
            this.style.color = '#333';
        }
        input.onblur = function(){
            if(this.value ==''){
                this.value = '手机';
            }
            this.style.color = "#999";
        }
    </script> 
1.2.3.4.2通过className进行样式属性修改

适用于样式较多或者功能复杂的情况
多类名选择器

<head>
<style>
        div{
            width:300px;
            height: 200px;
            background-color:aqua;
        }
        .change{
            margin-top: 300px;
            background-color: purple;
        }
</style>
</head>

<body>
	<div>文本</div>
    <script>
        var div = document.querySelector('div');
        div.onclick = function(){
            this.className = 'change';         //className会直接更改元素的类名,会覆盖原先的类名
            //如果想要保留原先的类名,使用多类名选择器
            this.className = '原先类名 change';
        }
        
    </script>
</body>
1.2.4 自定义属性值的操作

1.获取属性值:
(1)element.属性 (获取内置属性值)
(2)element.getAttribute(‘属性’); (主要获得自定义的属性)
2.设置属性值
(1)element.属性 = ‘值’
(2)element.setAttribute(‘index’,2)

///这里index是我们程序员自定义的一个属性值
<div id = "demo" index='1'></div>
<script>	
		var div = document.getElementById('demo');
		console.log(div.getAttribute('index'));
		div.setAttribute('index','2');
		div.className = 'navs';
		div.setAttribute('class','footer');      //注意:这里用的是'class',而不是'className'
</script>

3.H5自定义属性
自定义属性需使用getAttribute函数,而因为很难分清是自定义的属性还是内置属性,所以H5规定自定义属性data-开头作为属性名并且赋值
例如 <div data-index='1'> </div>
H5新增element.dataset.index或者element.dataset[‘index’]来获取data-index属性,但是这种方式需要再ie11之后
如果是 data-list-name,则使用驼峰命名法:element.dataset.listName

1.2.5节点操作

为什么要使用节点操作:利用DOM提供的方法获取元素时逻辑性不强且比较繁琐

利用节点层级关系获取元素:
(1)利用父子兄节点关系获取元素
(2)逻辑性强,但是兼容性差

网页中的所有内容都是节点,在DOM中,节点使用node来表示
节点至少拥有nodeTypenodeNamenodeValue这三个基本属性
(1)元素节点:nodeType为1
(2)属性节点:nodeType为2
(3)文本节点:nodeType为3(包含了文字、空格、换行等)
实际开发中,节点操作主要操作的是元素节点

节点层级常见的是父子兄层级关系

1.2.5.1父子节点

父取子: parentNode.children,可以用下标去访问
子取父:node.parentNode

<div class="demo">
    <div class="box">
        <span class="erweima"> </span>
    </div>
    </div>
    <script>
        var erweima = document.querySelector('.erweima');
        //得到的是最近的父节点,找不到为null
        var box = erweima.parentNode;
        console.log(box);
        //子节点     childNodes所有的子节点包含元素节点、文本节点(例如换行符)等等
        //所以一般不提倡使用childNodes
		console.log(box.childNodes);
		
		//使用parentNode.children获得元素节点
		console.log(box.children);
		
		//1.使用parentNode.firstChild 获取第一个子节点,包含了文本节点
		console.log(box.firstChild);
		//2.使用parentNode.firstElementChild 获取第一个子节点,不包含了文本节点,ie9才支持
		console.log(box.firstElementChild);
		//3实际开发的写法,既没有兼容性问题,又返回第一个子元素
		console.log(box.children[0]);
		console.log(box.children[box.children.length-1]);
		 
        
    </script>
1.2.5.2兄弟节点

nextElementSibling 下一个兄弟元素节点
previousElementSibling 得到上一个兄弟元素节点

//1.nextSibling 下一个兄弟节点,包含元素节点和文本节点等
console.log(div.nextSibling);
//2.nextElementSibling 得到下一个兄弟元素节点    (IE9以上才支持)
//3.previousElementSibling    得到上一个兄弟元素节点     (IE9以上才支持)
//4.自己封装一个函数
<script>
        function getNextElementSibling(element){
            var el = element;
            while(el = el.nextSibling){
                if(el.nodeType == 1){
                    return el.
                }
            }
            return null;
        }
</script>

1.2.5.3创建、添加和删除节点

动态创建元素节点
我们想要页面添加一个新的元素:1.创建元素 2.添加元素

<ul>
        <li>123</li>
    </ul>
    <script>
        var li = document.createElement('li');
        var ul = document.querySelector('ul');
        //后面追加元素
        ul.appendChild(li);
        var lili = document.createElement('li');
        ul.insertBefore(lili,ul.children[0]);
        
        //删除节点
        ul.removeChild(ul.children[0]);
    </script>
1.2.5.4留言案例
<textarea name="" id=""></textarea>
    <button>发布</button>
    <ul>
        
    </ul>
    <script>
        var text = document.querySelector('textarea');
        var btn = document.querySelector('button');
        var ul = document.querySelector('ul');
        btn.onclick = function(){
            if(text.value==''){
                alert('输入为空');
                return false;
            }
            else{
                var li = document.createElement('li');
                li.innerHTML = text.value+"<a href='javascript:;'>删除</a>";
                ul.insertBefore(li,ul.children[0]);
                //删除元素
                var as = document.querySelectorAll('a');
                for(var i = 0;i<as.length;i++){
                    as[i].onclick = function(){
                        ul.removeChild(this.parentNode);
                    }
                }
            }
        }
    </script>
1.2.5.5节点复制

深拷贝与浅拷贝

	<ul>
        <li>1</li>
        <li>2</li>
        <li>3</li>
    </ul>
    <script>
        var ul = document.querySelector('ul');        
        // cloneNode()不带true则为浅拷贝,只复制标签而不复制里面的内容
        var lili = ul.children[0].cloneNode()
        
        // cloneNode(true)  为深拷贝
        var lilili = ul.children[0].cloneNode(true);
        ul.appendChild(lili);
        ul.appendChild(lilili);
    </script>

(1)document.write是直接将内容写入页面的内容流,但是文档流执行完毕,则会导致页面全部重绘
(2)innerHTML是将内容写入某个DOM节点,不会导致页面重绘
(3)innerHTML创建多个元素效率更高(不要拼接字符串,采用数组形式拼接),结构稍微复杂
(4)createElement()创建多个元素效率稍低一点点,但是结构更清晰

1.3事件高级
1.3.1 注册事件

(1)传统注册方式;利用on开头的事件onclick
特点:注册事件的唯一性,同一个元素同一个事件只能设置一个处理函数,最后注册的处理函数将会覆盖前面注册的处理函数
(2)方法监听注册方式:

	<button>方法监听注册事件</button>
    <script>
        //事件侦听注册事件,里面的时间类型是字符串,必定加引号,而且不带on
        var btn = document.querySelector('button');
        btn.addEventListener('click',function(){
            alert(22);
        })
        //同一个元素,同一个事件可以添加多个侦听器(事件处理程序)
        btn.addEventListener('click',function(){
            alert(33);
        })
    </script>
1.3.2 解绑事件

(1)传统方式:eventTarget.onclick = null;
(2)方法监听方式:eventTarget.removeEventListener(type,listener[,useCapture]);

<div>1</div>
    <script>
        var div = document.querySelector('div');
        div.onclick = function(){
            alert(22);
            //1.传统方式
            this.onclick = null;
        }
        //2.removeEventListener方式
        div.addEventListener('click',fn);
        function fn(){
            alert(22);
        }
        div.removeEventListener('click',fn);
1.3.3 DOM事件流

事件发生时会在元素节点之间按照特定的顺序传播,这个传播过程即DOM事件流
DOM事件流分为3个阶段:1.捕获阶段 2.当前目标阶段 3.冒泡阶段
事件冒泡:事件开始时由最具体的元素接收,然后逐级向上传播到DOM最顶层节点的过程
事件捕获:由DOM最顶层节点开始,然后逐级向下传播到最具体的元素接收的过程

<div class="father" style="height: 200px;width: 200px;background-color: aqua;">
        <div class="son" style="height: 100px;width: 100px;background-color: rgb(161, 38, 61);">
        </div>			      
</div>
    <script>
        //1.js代码中只能执行捕获或者冒泡其中的一个阶段
        //2.onclick和attachEvent只能得到冒泡阶段
        //3.捕获阶段 如果是addEventListenner,第三个参数是true
        var son = document.querySelector('.son');
        son.addEventListener('click',function(){
            alert('son');
        },true);
        var father = document.querySelector('.father');
        father.addEventListener('click',function(){
            alert('father');
        },true);
        //4.冒泡阶段,如果是addEventListenner,第三个参数是false或者为空
        var son = document.querySelector('.son');
        son.addEventListener('click',function(){
            alert('son');
            //阻止冒泡,DOM推荐的标准 stopPropagetion()
            e.stopPropagation();
        },false);
        var father = document.querySelector('.father');
        father.addEventListener('click',function(){
            alert('father');
        },false);
    </script>
1.3.4 事件对象

事件对象是系统给我们自动创建的,不需要我们传递参数。
事件对象是我们事件的一系列相关数据的集合,比如鼠标点击这个事件中就包含了鼠标的相关信息,比如鼠标坐标,如果是键盘事件就包含键盘事件的信息,比如判断用户按下哪个键

	<button>123</button>
    <script>
        var btn = document.querySelector('button');
        //这个事件对象我们可以自己命名,开发中用e就行了
        btn.onclick = function(e){
            console.log(e);
        }
    </script>
e.target与this的区别:

(1)e.target返回的是触发事件的对象
(2)this返回的是绑定事件的对象

	<ul>   111111
        <li>2222222</li>
    </ul>
    <script>
        var ul = document.querySelector('ul');
        //onclick绑定的是ul,但是点击li,是li触发的,所以e.target是li
        ul.onclick = function(e){
            console.log(this);
            console.log(e.target);
            //阻止默认行为
            e.preventDefault();          
        }
    </script>
1.3.5 事件委托

事件委托也称事件代理,在jQuery里面称为事件委派。
原理:不是每个子节点单独设置事件监听器,而是事件监听器设置在其父节点上,然后利用冒泡原理影响设置每个子节点。

例如在ul中的li,我们在ul中注册点击事件,因为点击li,事件会冒泡到ul上,ul有注册事件,就会触发事件监听器,然后利用事件对象的target来找到当前点击的li进行操作

2.BOM

BOM(Browser Object Model)浏览器对象模型,它提供了独立于内容而与浏览器窗口进行交互的对象,其核心对象是window.

window对象有两重角色:
1.JS访问浏览器窗口的一个接口
2.它是一个全局对象,定义在全局作用域中的变量、函数都会变成window对象的属性和方法

window.onload当文档内容完全加载完成会触发该事件,这样就可以 把js代码写到页面元素的上方,因为onload是等页面内容全部加载完毕,再去执行处理函数。
传统注册事件方式只能写一个,以最后一个为准,所以可以使用window.addEventListener,这样就没有限制

<button>按钮</button>
    <script>
        window.onload = function(){
            var btn = document.querySelector('button');
            btn.addEventListener('click',function(){
                alert('点击了我');
            })
        }
        //load需要等页面内容全部加载完毕,DOMContentLoaded是DOM加载完毕,不包含图片、flash、css等就可以执行,加载速度更快
        document.addEventListener('DOMContentLoaded',function() {
        	alert(33);
        })
    </script>

2.1 调整窗口大小事件

<div style="width: 200px;height: 200px;background-color: aqua;"></div>
    <script>
        window.addEventListener('load',function(){
            var div = document.querySelector('div');
            window.addEventListener("resize",function(){
                if(window.innerWidth<=600){
                    div.style.display = 'none';
                }
                else{
                    div.style.display = 'block';
                }
            })
        })
    </script>

2.2 定时器

setTimeout(函数名,毫秒数);
//页面中可能有很多的定时器,我们经常给定时器加标识符

//clearTimeout停止定时器
clearTimeout(定时器标识符);

//setInterval  是反复地调用
setInterval(function(){
	console.log('继续输出');
},1000);

//clearInterval(定时器标识符); 

setTimeout与setInterval的区别
setTimeout 方法用于在一定的延迟之后执行一次指定的函数。
setInterval 方法用于在一定的时间间隔内重复执行指定的函数

2.3 JS执行机制

JS语言的一大特点就是单线程,后来JS出现了同步和异步。
同步任务都在主线程上执行,形成一个执行栈
JS的异步是通过回调函数实现的,一般有以下三种类型:
(1)普通事件,如click、resize等
(2)资源加载,如load、error等
(3)定时器,包括setInterval、setTimeout等
异步任务相关回调函数添加到任务队列(也称消息队列)中

过程:
(1)先执行执行栈中的同步任务
(2)异步任务放在消息队列中
(3)一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行。

由于主线程不断地重复获取任务、执行任务、获取任务、执行任务,这种机制被称为事件循环

三、ES6

面相对象

面相对象 vs 面相过程

在这里插入图片描述

ES6中的类和对象

在js中,可以使用class关键字声明一个类。对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如字符串、数值、数组、函数等。

类constructor构造函数

和Java等其他面向对象语言类似,没有创建系统会自带一个默认的构造函数

	<script>
        class Star {
            constructor(uname) {	
                this.uname = uname;
            }
        }
        var ldh = new Star('ldh');
        console.log(ldh.uname);
    </script>
类中添加方法
<script>
        class Star {
            constructor(uname) {
                this.uname = uname;
            }     //类中不加逗号
            sing(){
                console.log('唱歌');
            }
        }
        var ldh = new Star('ldh');
        console.log(ldh.uname);
    </script>
类的继承

使用:

<script>
        class Father {
            money() {
                console.log(1000);
            }
        }
        class Son extends Father {

        }
        var son = new Son();
        son.money();
    </script>

下面代码是有问题的:父类的sum函数用的是this.x,this.y,子类不能直接使用父类的sum(子类继承了父类时,如果定义了构造函数,那么构造函数必须通过 super() 调用父类的构造函数,以确保父类的属性得到正确的初始化。)

<script>
        class Father {
            constructor(x, y) {
                this.x = x;
                this.y = y;
            }
            sum() {
                console.log(this.x + this.y);
            }
        }
        class Son extends Father {
            constructor(x, y) {
                this.x = x;
                this.y = y;
            }
        }
        var son = new Son(10, 20);
        son.sum();
    </script>
super关键字

解决办法: 使用super()调用父类的构造函数

		class Son extends Father {
            constructor(x, y) {
                super(x, y); //调用父类的构造函数
            }
        }

super()不仅可以调用父类的构造函数,还可以调用父类的普通函数

<script>
        class Father {
            constructor(x, y) {
                this.x = x;
                this.y = y;
            }
            say() {
                console.log('我是你爸');
            }
        }
        class Son extends Father {
            constructor(x, y) {
                super(x, y); //调用父类的构造函数
            }
            say() {
                super.say();
            }
        }
        var son = new Son(10, 20);
        son.say();
    </script>

子类对象在调用函数时,遵循就近原则,如果子类中有,则调用子类中的函数,子类中没有则调用父类中的函数。但是也使用super关键字来主动去调用父类函数。

super关键字在this之前:

<script>
        class Father {
            constructor(x, y) {
                this.x = x;
                this.y = y;
            }
            add(x, y) {
                console.log(this.x + this.y);
            }
        }
        class Son extends Father {
            constructor(x, y) {
                //调用父类的构造函数
                //super必须在子类this之前调用
                super(x, y);
                this.x = x;
                this.y = y;
            }
            sub(x, y) {
                console.log(this.x - this.y);
            }
        }
        var son = new Son(10, 20);
        son.add();
        son.sub();
    </script>

如果在子类中把this.x=x和this.y=y;注释掉,则子类中的this将不会被初始化,执行sub实际上使用的是父类的this.x和this.y(因为用了super(x,y))

<script>
        class Father {
            constructor(x, y) {
                this.x = x;
                this.y = y;
            }
            add(x, y) {
                console.log(this.x + this.y);
            }
        }
        class Son extends Father {
            constructor(x, y) {
                //调用父类的构造函数
                //super必须在子类this之前调用
                super(x + 1, y + 10);
                // this.x = x;
                // this.y = y;
            }
            sub(x, y) {
                console.log(this.x - this.y);
            }
        }
        var son = new Son(10, 20);
        son.add();
        son.sub();
    </script>

输出结果为:
在这里插入图片描述

类使用时的注意点:
(1)ES6中没有变量提升,所以必须先定义类,才能通过类实例化对象
(2)类中共有的属性和方法一定要加this使用

类里面的this指向问题

constructor里面的this指向实例对象,方法里面的this指向了这个方法的调用者
原型对象this指向:原型对象的this指向的也是对象实例

class Father {
            constructor(x, y) {
                this.x = x;
                this.y = y;
                this.btn = document.querySelector('button');
                this.btn.onclick = this.add(this.x, this.y);
            }
            add(x, y) {
                //这个add方法里面的this指向的是btn这个按钮,因为这个按钮调用了这个函数
                console.log(this);
                // console.log(this.x + this.y);     //这行输出位undifined
            }
        }
        class Son extends Father {
            constructor(x, y) {
                //调用父类的构造函数
                //super必须在子类this之前调用
                super(x, y);
                this.x = x;
                this.y = y;
            }
            sub(x, y) {
                console.log(this.x - this.y);
            }
        }
        var father = new Father(10, 20);
    </script>

如果确实想在add()函数中使用this.x和this.y,则可以在外部定义一个全局变量赋值为constructor中的this。

<button>点击按钮</button>
    <script>
        var that;
        class Father {
            constructor(x, y) {
                that = this;
                this.x = x;
                this.y = y;
                this.btn = document.querySelector('button');
                this.btn.onclick = this.add;
            }
            add(x, y) {
                //这个sing方法里面的this指向的是btn这个按钮,因为这个按钮调用了这个函数
                console.log(this);
                console.log(that.x + that.y);
                // console.log(this.x + this.y);
            }
        }
        class Son extends Father {
            constructor(x, y) {
                //调用父类的构造函数
                //super必须在子类this之前调用
                super(x, y);
                this.x = x;
                this.y = y;
            }
            sub(x, y) {
                console.log(this.x - this.y);
            }
        }
        var father = new Father(10, 20);
    </script>
构造函数

在这里插入图片描述
在这里插入图片描述
静态成员:Star.sex='男'

构造函数原型对象prototype

原型的作用就是共享方法
构造函数通过原型分配的函数是所有对象所共享的。
每一个构造函数都有一个prototype属性,指向另一个对象。
把那些不变的方法,直接定义在prototype对象上,这样所有对象的实例就可以共享这些方法了。

<script>
        function Star(name,age){
            this.name = name;
            this.age = age;
        }
        Star.prototype.sing=function(){
            console.log('我会唱歌');
        }
        var ldh = new Star('ldh',18);
    </script>
对象原型

对象都有一个属性__proto__,指向了我们构造函数的原型对象

//结果为true
console.log(ldh.__proto__ === Star.prototype);  

所以方法的查找规则:首先看对象本身是否有该方法,有则执行,没有则因为有__proto__的存在,就去构造函数原型对象prototype身上去查找该方法

它是一个非标准属性,实际开发中,不可以使用这个属性

constructor 构造函数

prototype和__proto__里面都有一个constructor属性,指回了构造函数本身。
所以constructor主要用于记录该对象引用于哪个构造函数

	Star.prototype.sing = function () {
            console.log('我会唱歌');
        }

有时候prototype要放多个函数,可以简写为:

		Star.prototype = {
            sing: function(){

            },
            dance: function(){
                
            }
        }

但是前一种写法是给prototype增加属性,而后一种是直接整体赋值,这会导致prototype里面失去了constructor属性,所以要增加一个constructor:

Star.prototype = {
            constructor: Star,
            sing: function () {

            },
            dance: function () {

            }
        }

在这里插入图片描述

原型链

在这里插入图片描述

对象成员查找规则

在这里插入图片描述

原型对象的应用:扩展内置对象
可以通过原型对象,对原来的内置对象进行扩展自定义的方法。

案例:通过原型对象给数组提供sum方法:

Array.prototype.sum = function(){
            var sum=0;
            for(var i=0;i<this.length;i++){
                sum+=this[i];
            }
            return sum;
        }
    </scrip
继承

ES6之前没有提供extends继承,我们可以通过构造函数+原型对象模拟实现继承,被称为组合继承
call()函数的作用:
(1)调用函数
(2)修改函数的this指向

<script>
        function fn(x, y) {
            console.log(this);
            console.log(x, y);
        }
        var x = 23;
        fn.call(x, 4, 5)
    </script>

在这里插入图片描述
注意: 第一个参数必须是要修改的this

利用父构造函数继承属性:
<script>
        function Father(uname,age){
            this.name = name;
            this.age = age;
        }
        function Son(uname,age){
            //this指向的是子构造函数的对象实例
            //使用call函数,将Father构造函数的this修改为子this
            Father.call(this,uname,age);
        }
    </script>
利用原型对象继承父类型的方法

对于父类的 Father.prototype.money函数,它是写在了原型对象上,而不是写在了构造函数里,所以子类并不能直接继承这个方法。
同时,如果只是使用Son.prototype = Father.prototype,则会导致二者一致了,而通常Son.prototype有其独特的方法,你在Son.prototype增加方法使得Father.prototype也增加了方法。

		function Father(uname, age) {
            this.name = name;
            this.age = age;
        }
        Father.prototype.money = function () {
            console.log(10000);
        }

        function Son(uname, age) {
            //this指向的是子构造函数的对象实例
            //使用call函数,将Father构造函数的this修改为子this
            Father.call(this, uname, age);
        }
        Son.prototype = new Father();
        Son.prototype.constructor = Son;
        Son.prototype.exam = function () {
            console.log('考试');
        }
        console.log(Son.prototype);
    </script>
类的本质:

类的本质还是一个函数,类就是构造函数的另一种写法
ES6的类的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已,所以ES6的类其实就是语法糖
语法糖就是一种便捷写法,简单理解,有两种方法可以实现同样的功能,但是一种写法更加清晰、方便,那么这个方法就是语法糖

ES5中新增的方法

数组方法

迭代方法:forEach()、map()、filter()、some()、every();

forEach:
	<script>
        var arr = [1, 2, 3];
        arr.forEach(function (value, index, array) {
            console.log(value);
            console.log(index);
            console.log(array);
        })
    </script>
filter方法

创建一个新数组,新数组中的元素是通过制定数组中符合条件的所有元素,主要用于筛选数组,它返回的是一个新数组

        var arr = [13, 234, 323];

        var arr_new = arr.filter(function (value, index, array) {
            return value >= 20;
        })
        console.log(arr_new);
some方法

some方法用于检测数组中的元素是否满足制定条件,返回的是一个布尔值,查找到就返回true,查找不到就返回false。
找到满足条件的元素则终止循环

<script>
        var arr = [13, 234, 223];

        if (arr.some(function (value) {
                return value >= 300;
            })) {
            console.log('有大于300的数');
        } else {
            console.log('没有大于300的数');
        }
    </script>

forEach与some的区别:
(1)在some的回调函数中遇到return true就是终止遍历,迭代效率更高
(2)而在forEach的回调函数中return true是不会终止遍历的
filter里面的return也不会终止迭代

every方法
<script>
        var arr = [13, 234, 223];

        if (arr.every(function (value) {
                return value >= 300;
            })) {
            console.log('arr中全部都是大于300的数');
        } else {
            console.log('并不全是');
        }
    </script>
map方法
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(num => num * 2);

map方法返回一个新数组,而forEach不返回值

字符串方法
trim方法

它并不会影响到原字符串,而是创建了一个新字符串
str.trim();

<script>
        var str = '   dsfj   ';
        console.log(str.trim());
        console.log(str);
    </script>
对象方法
defineProperty方法

Object.defineProperty(obj,prop,descriptor)

<script>
        var obj = {
            id: 1,
            name: 'cxk'
        }
        Object.defineProperty(obj, 'age', {
            value: 28,
        })
        console.log(obj);
        //writable为false表示属性不可改
        Object.defineProperty(obj, 'id', {
            writable: false,
        })
        obj.id = 2;
        console.log(obj);
        //enumerable
        Object.defineProperty(obj, 'name', {
            enumerable: false, //值为false 则不允许遍历,默认的值是false
        })
        console.log(Object.keys(obj));
        //configurable为false表明不能被删除,不允许修改第三个参数里的特性,默认为false
        Object.defineProperty(obj,'name',{
            configurable: false,
        })
    </script>

函数进阶

函数的定义方式

(1)命名函数:function fn(){}; 相当于是第(3)种的实例
(2)匿名函数:var fun = function(){};
(3)利用new Function(‘参数1’,‘参数2’,‘函数体’); 不怎么使用

//必须全部用引号引起来
        var fn = new Function('a', 'b', 'console.log(a+b)');
        fn(2, 3);

所有的函数都是Function的实例(对象),而实例就有__proto__

函数的调用方式

(1)普通函数: fn(); fn.call();
(2)对象的方法:obj.fn();
(3)构造函数: new Star();
(4)绑定事件函数:btn.onclick=function(){};
(5)定时器函数: setInterval(function(){},1000);
(6)立即执行函数:
立即调用函数(Immediately Invoked Function Expression,IIFE)是 JavaScript 中的一种常见模式,用于创建一个立即执行的函数。这种模式的主要目的是创建一个独立的作用域,以避免变量污染全局作用域,同时执行其中的代码块。
(1)立即执行:函数定义后紧跟一对括号 (),这样函数会立即执行。
(2)独立作用域:函数内部声明的变量是局部变量,不会污染全局作用域。在示例中,localVar 是一个局部变量,只能在函数内部访问。
(3)避免命名冲突:立即调用函数创建了一个独立的作用域,可以防止与全局作用域中已有的变量或函数产生冲突。

<script>
        //立即执行函数是自动调用
        (function(){
            console.log('11');
        })()
    </script>
函数内部的this指向

在这里插入图片描述

改变this指向的方法:

(1)call函数
(2)apply函数:apply函数也是既可以调用函数,又可以改变函数内部的this指向,但是它的参数必须是数组(伪数组)
它的主要应用是:比如利用apply借助于数学内置对象求最大值

<script>
        var arr = [23, 3, 343, 23];
        console.log(Math.max(arr));  //输出为NaN
        console.log(Math.max.apply(null, arr));
    </script>

(3)bind函数:不会调用函数,但是能改变函数内部的this指向,返回由指定的this值和初始化参数改造的原函数拷贝
应用场景:有的函数我们不需要立即调用,但是又想改变这个函数内部的this指向

例如我们想设置一个按钮,当点击之后,就禁用这个按钮,3秒后开启这个按钮
以下方法不行:

		var btn = document.querySelector('button');
        btn.onclick=function(){
            this.disabled = true;
            //定时器函数里的this指向的是window,而我们要设置的是btn这个按钮
            setTimeout(function(){
                this.disabled=false;
            },3000)
        }

改为:

var btn = document.querySelector('button');
        btn.onclick=function(){
            this.disabled = true;
            //定时器函数里的this指向的是window,而我们要设置的是btn这个按钮
            setTimeout(function(){
                this.disabled=false;
            }.bind(this),3000)       //将setTimeout里面的回调函数的this改为外面btn的this
        }

三个按钮的情况:

		var btns = document.querySelectorAll('button');
        for (var i = 0; i < btns.length; i++) {
            btns[i].onclick = function () {
                this.disabled = true;
                setTimeout(function () {
                    //不能使用btns[i]的原因是setTimeout要等2秒运行,此时外层i已经++了
                    // btns[i].disabled=true;
                    this.disabled = false;
                }.bind(this), 2000)
            }
        }
严格模式
严格模式的使用

js除了提供正常模式外,还提供了严格模式(strict mode)
严格模式对正常的js语义做了一些更改:
(1)消除了js语法的一些不合理、不严谨之处,减少了一些怪异行为
(2)消除了代码运行的一些不安全之处,保证代码运行的安全
(3)提高编译效率,加快运行速度
(4)禁用了在ECMAScript的未来版本中可能会定义的一些语法,如一些保留字:class、enum等不能做变量名
严格模式可以应用到整个脚本个别函数中。
使用 'use strict'

<script>
        'use strict'
        //下面的js代码就会按照严格模式执行
    </script>

    <script>
        (function(){
            'use strict'
        })();
    </script>
严格模式的变化

(1)对于变量:
1.变量没有声明就赋值在严格模式下会被禁止
2.严禁删除已经声明的变量。例如 delete x; 语法是错误的。
(2)this指向的问题:
1.严格模式下全局作用域中函数的this是undefined(而之前是window)
2.严格模式下,构造函数不加new调用,this会报错 (之前可以当普通函数,this指向全局对象)
3.new实例化的构造函数还是指向创建的对象实例。
4.定时器this还是指向window
5.事件、对象还是指向调用者
(3)函数变化:
1.函数必须声明在顶层,新版本的js会引入块级作用域,不允许在非函数的代码块内声明函数。
2.函数不能有重名的参数

<script>

        function fn(a, a) {
            return a + a;
        }
        console.log(fn(1, 2));   //最终输出结果为4
    </script>

更多严格模式的变化请参照以下链接:严格模式

高阶函数

高阶函数是对其他函数进行操作的函数,它接收函数作为参数或将函数作为返回值输出。最典型的就是回调函数

闭包

变量作用域:
(1)函数内部可以使用全局变量
(2)函数外部不可以使用局部变量
(3)当函数执行完毕后,本作用域内的局部变量会销毁
闭包指有权访问 另一个函数作用域中的变量 的函数
简单理解,一个作用域可以访问另外一个函数内部的局部变量
闭包的作用:延伸了变量的作用范围

	<script>
        function fn() {
            var num = 10;

            function fun() {
                console.log(num);
            }
            return fun;
        }
        var f = fn();
        //上面的赋值相当于是
        // var f = function fun(){
        //     console.log(10);
        // }
        f();
        //此时调用f();执行了console.log(num),相当于访问了fn作用域下的num
    </script>

闭包应用-点击li输出当前li的索引号:
以下这种写法是有问题的:onclick是异步,而外层的for循环是同步的,这会导致输出的永远是3

	<ul class="nav">
        <li>1</li>
        <li>2</li>
        <li>3</li>
    </ul>
    <script>
        var lis = document.querySelector('.nav').querySelectorAll('li');
        for(var i=0;i<lis.length;i++){
            lis[i].onclick = function(){ 
                console.log(i);
            }
        }
    </script>

以前的方式:

<script>
        var lis = document.querySelector('.nav').querySelectorAll('li');
        for (var i = 0; i < lis.length; i++) {
            lis[i].index = i;
            lis[i].onclick = function () {
                console.log(this.index);
            }
        }
    </script>

现在使用闭包的方式:

<script>
        var lis = document.querySelector('.nav').querySelectorAll('li');
        for (var i = 0; i < lis.length; i++) {
            (function (i) {
                lis[i].onclick = function () {
                    console.log(i);
                }
            })(i);

        }
    </script>

以下没有闭包产生:

	<script>
        var name = "The window"
        var object = {
            name: "My Object",
            getNameFunc: function () {
                return function () {
                    return this.name;
                }
            }
        }
        console.log(object.getNameFunc()());
    </script>

以下有闭包产生:

	<script>
        var name = "The window"
        var object = {
            name: "My Object",
            getNameFunc: function () {
                var that = this;
                return function () {
                    return that.name;
                }
            }
        }
        console.log(object.getNameFunc()());
    </script>
拷贝

浅拷贝与深拷贝
浅拷贝: Object.assign(target,sources);
深拷贝:

		function deepCopy(newobj,oldobj){
            for(var k in oldobj){
                var item=oldobj[k]
                if(item instanceof Array){
                    newobj[k]=[];
                    deepCopy(newobj[k],item);
                }
                else if(item instanceof Object){
                    newobj[k]={};
                    deepCopy(newobj[k],item);
                }
                else{
                    newobj[k]=item;
                }
            }
        }

正则表达式

正则表达式是用于匹配字符串中字符组合的模式,在js中,正则表达式也是对象
一些使用场景
在这里插入图片描述

创建正则表达式

(1)通过RegExp对象来创建:var regexp = new RegExp(/123/)
(2)使用字面量来创建: var rg = /123/;

测试正则表达式
		var regexp = new RegExp(/123/)
        var rg = /123/;
        console.log(rg.test(32));
正则表达式中的特殊字符

一个正则表达式可以由简单的字符构成,比如/abc/,也可以是简单字符和特殊字符的组合,比如/ab*c/ 其中特殊字符也被称为元字符,在正则表达式中是具有特殊意义的专用符号,如^、$、+等

边界符

边界符用来提升字符所处的位置,主要有两个字符
在这里插入图片描述

		var rg = /^123/;
        var reg = /123$/
        var rgg = /^123$/
        var rggg = /[abc]/ //[]表明只要包含a,或者包含有b,或者有c都返回true
        rggg = /^[abc]$/   //三选一,a,b ,c
        rggg = /^[a-z]$/   //26选一
        rggg = /^[a-zA-X0-9_-]$/
        //[]里面的^不是边界符的含义,而是取反
        rggg = /^[^a-zA-X0-9_-]$/    
         
量词符

在这里插入图片描述

		var reg = /^a*$/;   //a取0次或n次
        var reg = /^a+$/;   //a取1次或n次
        var reg = /^a?$/;   //a取0次或1次
        var reg = /^a{3}$/; //a刚好取3次
        var reg = /^a{3,}$/; // a>=3
        var reg = /^a{3,16}$/; // a>=3 且 a<=16
        var reg = 

小括号代表优先级

        var reg = /^abc{3}$/    //让c重复3次
        var reg = /^(abc){3}$/  //让abc重复3次
正则表达式在线匹配

正则表达式在线匹配

预定义类

预定义类指的是某些常见模式的简写方式

在这里插入图片描述

//座机号码两种格式   010-12345678 0530-1234567
var reg = /^\d{3}-\d{8}| \d{4}-\d{7}$/
正则表达式中的替换

正则表达式参数:/表达式/[switch]
switch有三种值: g表示全局匹配,i表示忽略大小写, gi表示全局匹配+忽略大小写

<textarea name="" id="message"></textarea><button>提交</button>
    <div></div>
    <script>
        var text = document.querySelector('textarea');
        var btn = document.querySelector('button');
        var div = document.querySelector('div');
        btn.onclick = function () {
            div.innerHTML = text.value.replace(/激情/g, '**');
        }
    </script>

ES6

ES6实际上是一个泛指,泛指ECMA2015及后续的版本

let

(1)let声明的变量只在所处于的块级才有效
在{}中,使用let关键字声明的变量才具有块级作用域,var是不具备这个特点的。

<script>
        if (true) {
            let a = 32;
            var b = 43;
        }
        console.log(b);
        console.log(a);
    </script>

(2)防止循环变量变成全局变量
使用var可以访问

<script>
        for(var i=0;i<3;i++){

        }
        console.log(i);       //能输出3
    </script>

使用let不能访问

<script>
        for (let i = 0; i < 3; i++) {

        }
        console.log(i);
    </script>

(3)使用let声明的变量不存在变量提升

<script>
        console.log(i);
        let i = 10;
    </script>

运行结果为:
在这里插入图片描述

<script>
        console.log(i);
        var i = 10;
    </script>

运行结果为undifined

(4)暂时性死区:指的是在代码块内部,尽管变量已经用 let 声明,但在声明之前访问该变量会引发 ReferenceError。这个暂时性死区直到变量声明的位置被执行才会解除。
我的理解是这个作用域内只要用let声明了一个变量,则在这个作用域最前面到这个let变量声明的位置都不能访问这个标识符,即使外层作用域里有同名的额变量

<script>
        var num = 123;
        if (true) {
            console.log(num);
            let num = 10;
        }
    </script>

经典面试题(1):
在这里插入图片描述
for循环里面的function并不会立即执行,而是等到arr0调用才执行,而变量i是全局变量,此时函数执行时输出的都是全局作用域下的值。

经典面试题(2):
在这里插入图片描述
此题的关键在于每次循环都会产生一个块级作用域,每个块级作用域中的变量都是不同的,函数执行时输出的是自己上一级(也就是循环产生的块级作用域)作用域下的值。

const

作用: 声明常量,常量就是值(内存地址)不能变化的量。
(1)具有块级作用域
(2)声明时必须赋值
(3)常量赋值后,值不可以修改,这有两种情况:
1.基本类型,不可以修改
2.复杂类型,属性,元素可以修改,但是不能整体赋值修改其内存地址

const num = 10;
        num = 34;   //报错,不能修改
        const arr = [3, 23, 32];
        arr[0] = 100;  //可以修改其中元素的值 
        arr = [4, 34, 23];   //报错,不能对其重新赋值
var、let、const区别

在这里插入图片描述

解构赋值
数组解构
        let arr = [23, 34, 435, 32]
        let [a, b, c, d, e] = arr;

解构失败的部分则为undifined

		let arr = [23, 34, 435, 32]
        let [a, b, c, d, e] = arr;       //e为undifined
对象解构

第一种写法

		let person = {
            name: 'cxk',
            age: 12
        }
        let {name,age} = person

第二种写法:

<script>
        let person = {
            name: 'cxk',
            age: 12
        }
        let {
            name: myName,
            age: myAge
        } = person
        console.log(myName);
        console.log(myAge);
    </script>
箭头函数

ES6新增的定义函数的方式
语法:()=> {}

	<script>
        const fn = () => {
            console.log(10);
        }
        fn();
    </script>

如果函数体中只有一句代码,且代码的执行结果就是返回值,则可以省略大括号

<script>
        const sum = (num1, num2) => num1 + num2;
        console.log(sum(2, 43));
    </script>

如果形参只有一个,那么小括号也可以省略

		const fn = v => {
            alert(v);
        }
        fn(3);
箭头函数的this

箭头函数不绑定this关键字,箭头函数中的this,指向的是函数定义位置的上下文this

        const obj = {
            name: '张三'
        };

        function fn() {
            console.log(this);
             //返回的匿名函数在fn函数中(根据上下文),fn函数的this指向的是obj,所以确定this为obj
            return () => {        
                console.log(this);
            }
        }
        const resFn = fn.call(obj);
        resFn();

代码执行结果:
在这里插入图片描述
箭头函数面试题:

var obj = {
            age: 20,
            say: () => {
                //var不能形成作用域,所以此时的this指向window,而window没有age,所以是undifined
                alert(this.age)
            }
        }
        obj.say();

能形成作用域的一些情况:
(1)函数:每个函数都会创建自己的作用域。
(2)块级作用域:使用 let 或 const 关键字可以在块级作用域内声明变量,如 if 语句、for 循环、while 循环以及花括号 {} 中
(3)模块:在 ES6 中,通过 import 和 export 可以创建模块作用域

四.jQuery

jQuery官网
下载文件后在html中导入:
在这里插入图片描述

  • 40
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值