JavaScript

JavaScript

JavaScript是一门弱类型脚本语言

合格后端人员,必须精通js

1、入门

1.1、基本语法

<script>

</script>

​ js代码写在script标签内

<script src="js/**.js"></script>

外部引入 另外写一个.js文件,通过文件路径引入,这里<scritpt>和</script>需成对出现,如果写成<script src=""/>有可能会有问题

​ js严格区分大小写

​ console.log(x)打印变量(浏览器控制台中调试用)

​ 审查元素sources中可以给代码打断点调试

1.2、变量

var a = "";

​ 变量名不可以用数字开头

​ 局部变量建议都用let去定义而不用var

1.3、数据类型(入门)

基本类型(值类型)

数字(Number)
111 //整数
1.111 //浮点数
1e2 //科学计数法
-1 //负数
NAN //not a number
Infinity //无限大
字符串(String)
'aaa'
"aaa"
布尔(Boolean)
true
false
空(null)

null 空

未定义(undefined)

undefined 未定义

Symbol

引用数据类型

对象(Object)
var person{
    name: "mingzi",
    age: "2.5",
    tags: ['js', 'java', '...']
}

对象使用大括号,数组使用中括号

每个属性之间用逗号隔开,最后一个不需要

如何取值:person.name

数组(Array)

​ 一系列类型相同的对象

var arr = [1,2,"3",'4',true,null];//尽量使用这种,保证代码可读性
new Array(1,2,"3",'4',true,null);

数组越界会得到undefined

函数(Function)

1.4、运算符

逻辑运算

&& 与运算|| 或运算! 非运算

比较运算符

= 赋值== 等于(类型不一样,值一样,也true)=== 绝对等于(类型一样,值一样,才会true)

尽量不用==比较

NaN===NaN //false

NaN与所有数值都不相等,包括自己

只能通过isNaN(NaN)来判断是否NaN

let a = 1;let b = "1";let c = 1;console.log(a!=b) //falseconsole.log(a!==b) //trueconsole.log(b!=c) //falseconsole.log(b!==c) //trueconsole.log(a!=c) //falseconsole.log(a!==c) //false

比较过程

!= 比较时,若类型不同,会尝试转换类型;

!== 只有相同类型才会比较。

比较结果

!=返回同类型值比较结果 ;

!== 不同类型不比较,且无结果,同类型才比较;

浮点数问题

console.log((1/3) ===(1-2/3)) //false

尽量避免使用浮点数进行运算,存在精度问题

console.log(Math.abs(1/3-(1-2/3)) < 0.0000000001) //true

无限接近0算相等

1.5、严格检查模式

'use strict';

严格检查模式,预防js随意性导致产生的问题

必须写在JavaScript的第一行

2、数据类型(详细)

2.1、字符串

转移字符

"\'""\"""\n" 换行"\t" tab"\u4e2d" Unicode编码"\x41" AscII字符

多行字符串编写

​ 用的是`不是单引号(波浪号那个键)

var msg = `啊啊啊啊啊啊`

模板字符串

let name = "shiro";let msg = `你好,${name}` //需要用`而不是单引号

字符串长度

str.length

字符串可变性

​ 不可变

大小写转换

str.toUpperCase(); //大写str.toLowerCase(); //小写

字符位置获取(indexOf)

​ str.indexOf(searchvalue,fromindex);

image-20210719153920821

image-20210719154012831

截取字符串(substring)

str.substring(start,stop)

image-20210719154126256

image-20210719154218609

前闭后开,即substring(1,3)为[1,3)

image-20210719154300492

只有一个参数时从参数位置开始截到最后

2.2、数组

​ Array,可以包含任意的数据类型

var arr = [1,2,3,4,5,6]

长度

arr.length

可以通过arr.length = 2来直接修改arr的长度,如果长度变大,会填充undefined值,如果改小,会丢失数据

indexOf

用法同字符串的indexOf

slice

同字符串的substring

splice

arr.splice(index,howmany,item1,.....,itemX)

image-20210719161556248

image-20210719161529993

push, pop

同栈的push和pop,往尾部压入和弹出元素

unshift, shift

和push,pop类似,往头部压入(unshift)和弹出(shift)元素

元素反转

arr.reverse()

concat

arr(4) [2, 5, "2", 2]arr.concat([2,34,5])(7) [2, 5, "2", 2, 2, 34, 5]arr(4) [2, 5, "2", 2]

并不会改变原来的数组,只返回一个新数组

join

arr(4) [2, 5, "2", 2]arr.join("-")"2-5-2-2"

打印数组,用特定字符连接

多维数组

image-20210719162854118

2.3、对象

JavaScript中所有的键都是字符串,值是任意对象

键值对描述属性 xxx: xxx

var 对象名 = {    属性名: 属性值,    属性名: 属性值}

对象赋值

image-20210719170657991

使用不存在对象时,不会报错,只会拿到undefined

person.ssundefined

动态删减属性

delete person.nametrueperson{age: 9, gender: "boy"}

动态添加

image-20210719172917167

判断属性是否存在这个对象中

"age" in persontrue//继承的方法也会返回true"toString" in persontrue

判断一个属性是否这个对象自身拥有

person.hasOwnProperty("age")trueperson.hasOwnProperty("toString")false

2.4、流程控制

if

while

for

都和java一样

for in

let age =[1,2,3,4,5,6,7,1,2,3,1,2,3];for(let num in age){    console.log(age[num]);}

遍历age里的值

for of

let age =[1,2,3,4,5,6,7,1,2,3,1,2,3];for(let num of age){    console.log(num);}

效果与上面for in 一样,for in 获取得到下标,for of直接得到值

forEach

let age =[1,2,3,4,5,6,7,1,2,3,1,2,3];age.forEach(function (value){    console.log(value);})

2.5、Map

let map = new Map([["shiro",100],["shirooil", 90]]);let score = map.get("shiro");console.log(score);///100
map.set("bbb",80);map.delete("bbb");

2.6、Set

​ 无序不重复的集合

let set = new Set([3,1,1,1,1,1]);

image-20210719175853186

3、函数

3.1、定义函数方式一:

function add(a,b){    return a+b;}add(1,2) //3

3.2、定义函数方式二:

let a = function(a,b){//匿名函数,把结果赋值给a    return a+b;}a(1,2) //3

​ 如果没有执行return,会返回undefined

3.3、参数问题

JavaScript可以传任意个参数,也可以不传参数

假设不存在参数,如何解决?

可以手动抛出异常

let a = function(a,b){    if(typeof a != "number")        throw "not a number";    return a+b;}

image-20210719182727732

3.4、argument

可以通过argument获取传进来的所有参数,argument是一个数组

image-20210719184039071

3.5、rest

通过rest获取传进来的除了已定义好的以外的所有参数,rest必须放在最后,且需...标识

let a = function(a,b,...rest){    console.log(arguments);    console.log(rest);}

image-20210719184811141

函数、变量作用域

​ 内部函数可以访问外部函数成员,外部函数不可访问内部函数成员

function a(){    let x = 1;    function b(){        let y = x +1;        console.log(y);//2    }    b()    let z = y+1;//undefined}

​ 在函数内定义的值,在函数外就没用了,互不相关

function a(){    let x = 1;    console.log(x);//先输出1再通过b输出2    b();}function b(){    let x = 2;    console.log(x);//只输出2}

​ JavaScript中函数查找变量从自身函数开始,由内向外查找,假设外部存在同名的变量,则内部函数会屏蔽外部函数的变量

    function a(){        let x = 1;        function b(){            let x = 2;            console.log("b"+x);//b2        }        console.log("a"+x);//a1        b();    }

​ 使用var会先定义后赋值,即使用到的变量在使用程序句的后面,

但使用let的时候就不行了

​ let必须先声明,在使用。而var先使用后声明也行,只不过直接使用但没有定义的时候,其值是undefined。var有一个变量提升的过程,当整个函数作用域被创建的时候,实际上var定义的变量都会被创建,并且如果此时没有初始化的话,则默认为初始化一个undefined

    function a(){        var x = "x"+y;        console.log(x);//xundefined        var y = 'y';    }//上下两个函数等价    function a(){        var y;        var x = "x"+y;        console.log(x);//xundefined        y = "y";    }

​ 提升变量的作用域,js执行引擎,自动提升了声明,但不会提升变量的赋值

全局变量
var x = 1;function f(){    console.log(x);}console.log(x);
全局对象window
var x =1;alert(x);//弹出1alert(window.x);//弹出1,所有全局变量默认绑定在window对象下

可以看出window被省略了,推断出alert也是window对象下的

window.alert(window.x);//弹出1var b = window.alert;b(x); //弹出1var a = window.alert(window.x);a;//弹出1

​ Js实际只有一个全局作用域,任何变量,假设没有在函数作用范围内找到,就会向外查找,如果在全局作用域都没有找到,就会报错

规范

​ 所有全局变量都会绑定到window上,如果不同js文件使用了相同的全局变量,会导致冲突

如何减少全局变量冲突?
var shiro = {};//唯一全局变量shiro.name = "shirooil";//定义变量shiro.age = "22";

image-20210720133352054

把变量全部放入自己定义的唯一命名空间中,减少全局变量冲突

局部作用域问题
for(var i = 0; i<10; i++)    console.log(i);console.log(i+1);//i出了for循环还可以使用,不应该出现这个问题

image-20210720140626590

var全局变量导致的问题,出了作用域还可以使用,因此使用let来解决局部作用域的问题

for(let i = 0; i<10; i++)    console.log(i);console.log(i+1);不可以继续使用了

image-20210720140734606

常量

​ ES6之前,定义常量的方法:全大写变量名,建议不要修改

​ ES6之后,引入了常量关键字 const

3.5、方法

​ 方法:把函数放在对象里,对象只包含属性和方法两个东西

var shiro = {    name : "shiro",    birth : 1999,    age: function (){        var now = new Date().getFullYear();        return now - this.birth;    }}shiro.name //"shiro"shiro.age() //22//方法一定要带(),否则返回的是一段函数
function get_Age(){    var now = new Date().getFullYear();    return now - this.birth;}var shiro = {    name : "shiro",    birth : 1999,    age: get_Age}shiro.age() //22get_Age() //NaN//this默认指向调用它的对象,如果直接调用则调用的window的对象console.log(get_Age.apply(shiro,[])); //22console.log(get_Age.apply(shiro)); //22console.log(get_Age.apply(lwr,[])); //25

apply可以控制this的指向,[]表示无参数,可省略

4、内部对象

标准对象

undefinedtypeof 123"number"typeof "123""string"typeof NaN"number"typeof null"object"typeof []"object"typeof {}"object"typeof undefined"undefined"typeof Math.abs"function"
typeof null"object"

为什么null的类型是object而不是null

这是JS语言本身的一个bug。

不同的对象在底层都表示为二进制,在js中二进制前三位都为0的话会被判断为object类型,null的二进制表示全是0,自然前三位也是0,所以执行typeof时返回"object"

image-20210720144135129

4.1、Date

var now = new Date(); //Tue Jul 20 2021 14:46:41 GMT+0800 (中國標準時間)now.getFullYear(); //年now.getMonth(); //月 0~11 0代表1月 11代表12月now.getDate(); //日 now.getDay(); //周几now.getHours(); //时now.getMinutes(); //分now.getSeconds(); //秒 now.getTime(); //时间戳 1626763601756new Date(1626763601756); //Tue Jul 20 2021 14:46:41 GMT+0800 (中國標準時間)

时间戳全世界统一,从1970年1月1日0点开始毫秒级计算

4.2、Json

  • JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。

  • 采用完全独立于编程语言的文本格式来存储和表示数据。

  • 简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。

  • 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。

    JS中一切皆为对象,任何JS支持的类型都可以用JSON来表示

    格式:

    • 对象{}
    • 数组[]
    • 键值对 key:value
var shiro = {    name : "shiro",    age : 2}var jsonshiro = JSON.stringify(shiro);shiro //{name: "shiro", age: 2}jsonshiro //"{\"name\":\"shiro\",\"age\":2}"var shiro_from_json = JSON.parse("{\"name\":\"shiro\",\"age\":2}")shiro_from_json //{name: "shiro", age: 2}

5、面向对象

JS的面向对象和其他的面向对象有区别

  • 类:模板 原型对象

  • 对象:具体的实例

在js中对于类和对象需要换下思维方式

原型:相当于继承

var shiro = {    name: "shiro",    run: function (){        console.log(this.name + "跑");    }}var lwr = {    name: "lwr"}var bird = {    name: "bird",    fly: function (){        console.log(this.name + "飞");    }}bird.__proto__ = shiro;//原型对象 bird的原型是shirolwr.__proto__ = bird;//lwr的原型是birdconsole.log(bird.run()) //bird跑console.log(lwr.run()) //lwr跑console.log(lwr.fly()) //lwr飞

class

ES6之后才有class

    var shiro = {        name: "shiro",        run: function (){            console.log(this.name + "跑");        }    }    class Student{        constructor(name) {            this.name = name;        }    }    class lwr extends Student{        constructor(name, age) {            super(name);            this.age = age;        }        get_age(){            console.log(this.age + "岁");        }    }    var lwr1 = new lwr("shiro", 11);    lwr1.get_age(); //11岁

image-20210720160453579

本质还是原型

原型链

img

6、操作BOM对象(重点)

JS和浏览器关系:JS诞生就是为了能够让他在浏览器中运行

BOM:浏览器对象模型

navigator

navigator对象可以获得浏览器信息,但是容易被人为修改,所以不推荐使用navigator对象的属性来判断和编写代码

location(重要)

​ location代表当前页面的URL信息

image-20210720165824479

location.assign("")会跳转到其他网页

location.assign("https://www.bilibili.com")

一般用来设置新的定位

document

​ document代表当前的页面

可以获取具体的文档树节点

<ol id="order_list">    <li>嘻嘻</li>    <li>哈哈</li></ol><script>    var list = document.getElementById("order_list");</script>

image-20210720170540245

document.cookie //获取cookie

image-20210720171157693

服务器端可以设置cookie: httpOnly来防止cookie被别人获取

7、操作DOM对象(重点)

获取节点

<div class="father1" id="father">    <h1>标题</h1><p id ="p1">啊啊</p><p class="p2">啊啊</p></div><script>    var h1 = document.getElementsByTagName("h1");var p1 = document.getElementById("p1");var p2 = document.getElementsByClassName("p2");var father = document.getElementById("father");var father1 = document.getElementsByClassName("father1");

这是原生代码,之后都用jQuery();

image-20210720173429456

getElementById 是通过id来获取元素,id在HTML中是唯一的,所以获取到的只有一个元素

getElementsByTagNam 是通过标签名来获取元素,一种标签在HTML中可以有多个,所以获取到的是多个元素,且返回是以集合的形式返回。

getElementsByClassName 是通过类名来获取元素,同名的类在HTML中也能存在多个,所以获取到的也是多个元素,同样是以集合的形式返回。

image-20210720173619109

father.children; //获取父节点下的所有节点,返回一个集合

更新节点

h1[0].innerText = "啊啊啊"//innerText只可以改变文本p1.innerHTML= "<h1>嘻嘻</h1>";//innerHTML可以改变标签等(可以解析HTML标签)//注意获取节点时拿到的是集合还是单个元素p1.style.color = "red";//变红色字体,记得属性是字符串

删除节点

​ 步骤:先获取父节点,通过父节点删除子节点

<div class="father1" id="father">    <h1>标题</h1>    <p id ="p1">啊啊</p>    <p class="p2">啊啊</p></div><script>    var h1 = document.getElementsByTagName("h1");    var p1 = document.getElementById("p1");    var p2 = document.getElementsByClassName("p2");    var father = document.getElementById("father");    father.removeChild(h1[0]);//切记集合和单个元素区别    father.removeChild(p1);            father.removeChild(father.children[0]);///还可以直接用下标删除,但删除是动态的,切记下标会跟着改变</script>

插入节点

​ 如果dom节点为空,直接innerHTML就可以增加元素,如果dom节点已经存在元素,就不能这样,否则会覆盖

追加

<p id="outer">外面</p><div class="father1" id="father">    <h1>标题</h1>    <p id ="p1">啊啊</p>    <p class="p2">啊啊</p></div><script>    var h1 = document.getElementsByTagName("h1");    var p1 = document.getElementById("p1");    var p2 = document.getElementsByClassName("p2");    var father = document.getElementById("father");    var p3= document.getElementById("outer");    father.appendChild(p3);

image-20210720180733458

实际效果会把原来的p3节点删除,所以这应该是移动节点的操作,如果要添加一个节点且原节点不删除的话,需要先克隆再移动克隆出来的新节点

var p4 = p3.cloneNode(true);father.appendChild(p4);

cloneNode() 方法克隆所有属性以及它们的值。

如果您需要克隆所有后代,请把 deep 参数设置 true,否则设置为 false

也可以创建一个新标签插入

var newp = document.createElement('p'); //创建一个新的p标签newp.id = "newp"; //给id赋值newp.innerText = "嘻嘻嘻嘻嘻"; //给newp文本赋值father.appendChild(newp);
var body_Get = document.getElementsByTagName("body");body_Get[0].style.background ="red";

可以获取body的标签来修改属性

var myStyle = document.createElement('style');myStyle.innerHTML = "\n" +    "        body{\n" +    "            background-color: red;\n" +    "        }"document.getElementsByTagName("head")[0].appendChild(myStyle);

效果同上修改body属性,可以看出js的添加节点可以干很多事情

insertbefore

father.insertBefore(p4,p1);//把p4这个节点插到p1之前

8、操作表单(验证)

var username = document.getElementById("username");

实时获得用户名输入框里输入的值

MD5加密要引入

<script src = "https://cdn.bootcss.com/blueimp-md5/2.10.0/js/md5.min.js"></script>
<body>    <form id = "form" action="#">        <p>            <span>用户名:</span> <input type="text" id="username" name="username">        </p>        <p>            <span>密码:</span> <input type="password" id="password" >        </p>        <input type="hidden" id="md5-password" name="password">        <button type="submit" onclick="aaa()">提交</button>    </form><script>    function aaa(){        var username = document.getElementById("username");        var pwd = document.getElementById("password");        var md5pwd = document.getElementById("md5-password");        md5pwd.value = md5(pwd.value);    }</script></body>

表单需要有name的标签才能提交,所以这段代码里实际提交的是隐藏的id为md5-password的数据

9、jQuery

中文手册:https://jquery.cuishifeng.cn/

含有大量JS代码的库

引入

 <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>

公式

$(selector).action()
<body>    <a href="" id="test">点我</a><script>    $('#test').click(function (){        alert("1");    })</script></body>
<script>    //JS    //标签选择器    document.getElementsByTagName();    //id选择器    document.getElementById();    //类选择器    document.getElementsByClassName();    //jQuery 顺序同上 css中的所有选择器jquery都能用    $('p').click();    $('#id').click();    $('.class').click();    </script>

事件

<script>    $(document).ready(function (){            })//网页加载完后执行的函数,简化后成下面这行    $(function (){//和上一句同义            })</script>

鼠标事件

<style>    #divMove{        width: 500px;        height: 500px;        border: 1px solid red;    }</style><body>mouse: <span id ="mouseMoves"></span><div id="divMove">    在这里移动鼠标试试</div><script>    $(function (){        $('#divMove').mousemove(function (e){            $('#mouseMoves').text('x:'+e.pageX+'y:'+e.pageY);        });    })</script></body>

操作Dom

节点文本操作

<ul id="ul1">    <li id ="li1">啊</li>    <li>啊啊</li></ul><script>    $('#ul1 li[id=li1]').text();//“啊” 获得值    $('#ul1 li[id=li1]').text("啊啊");//设置值    $('#ul1 li[id=li1]').html();//“啊” 获得值    $('#ul1 li[id=li1]').html("<strong>啊啊</strong>");//设置值</script>

CSS操作

$('#ul1 li[id=li1]').css("color","red");// 字变红

元素的显示和隐藏,本质:使display:none;切换

$('#ul1 li[id=li1]').show();$('#ul1 li[id=li1]').hide();

拓展

闭包

闭包:一个函数和它的周围状态的引用捆绑在一起的组合

如果面试官问到闭包
关键词:词法作用域
加分项:执行上下文机制 V8垃圾回收机制

//函数作为返回值function test(){    const a = 1;    return function(){        console.log(a)    }}const fn = test()const a = 2fn()//1//函数作为参数function test(fn){    const a = 1    fn()}const a = 2function fn(){    console.log(a)//2}test(fn)

执行上下文

javascript 中“执行上下文”的类型?

  • 全局执行上下文:只有一个,也就是浏览器对象(即window对象),this指向的就是这个全局对象。

  • 函数执行上下文:有无数个,只有在函数被调用时才会被创建,每次调用函数都会创建一个新的执行上下文。

  • Eval函数执行上下文:js的eval函数执行其内部的代码会创建属于自己的执行上下文, 很少用而且不建议使用。

词法作用域和动态作用域的区别

​ 词法作用域(静态作用域)是在书写代码或者说定义时确定的,而动态作用域是在运行时确定的。

词法作用域关注函数在何处声明,而动态作用域关注函数从何调用,其作用域链是基于运行时的调用栈的。

var a = 2;function foo() {    console.log( a );}function bar() {    var a = 3;    foo();}bar();//如果是词法作用域,输出2。如果是动态作用域,输出3

作用域

let声明的变量是块级作用域,var声明的变量是函数作用域

// 函数function a(){	var i = 0;	console.log(i) // i = 0;}console.log(i) // i is not define// 块{	var j = 0;	console.log(j)  // j = 0}console.log(j)  // j = 0;
// 和var声明变量的例子比较{	let i = 0;	console.log(i)  // i = 0;}console.log(i) // error 

重定义this(call()、apply()、bind())

image-20211128214847165

Promise

​ Promise属于JavaScript引擎内部任务,而setTimeout则是浏览器API,而引擎内部任务优先级高于浏览器API任务,所以有此结果。

判断页面加载完成

js

// (1)、页面所有内容加载完成执行window.onload = function(){    }// (2)、ie9以上版本监听事件if('addEventListener' in document){    document.addEventListener('DOMContentLoaded', function(){            }, false)//false代表在冒泡阶段触发,true在捕获阶段触发}// (3)、页面加载完毕document.onreadystatechange = function(){    if(doucument.readyState == 'complete'){        // 页面加载完毕    }}注:js方法没有依赖方法简单,但方法(2)存在兼容性问题。

Jquery

$(function(){})$(document).ready(function(){    // document 不写默认document})注:jquery方法兼容性好,并且实在dom资源加载完毕的情况下执行,(不包括图片视频资源)

Vue

mounted () {  this.$nextTick(() => {    // 确保dom异步加载完毕  })}

Array.prototype.slice.call()方法详解

Array.prototype.slice.call(arguments)能将具有length属性的对象转成数组,除了IE下的节点集合(因为ie下的dom对象是以com对象的形式实现的,js对象与com对象不能进行转换)

var a={length:2,0:'first',1:'second'};//类数组,有length属性,长度为2,第0个是first,第1个是secondconsole.log(Array.prototype.slice.call(a,0));// ["first", "second"],调用数组的slice(0);var a={length:2,0:'first',1:'second'};console.log(Array.prototype.slice.call(a,1));//["second"],调用数组的slice(1);var a={0:'first',1:'second'};//去掉length属性,返回一个空数组console.log(Array.prototype.slice.call(a,0));//[]function test(){  console.log(Array.prototype.slice.call(arguments,0));//["a", "b", "c"],slice(0)  console.log(Array.prototype.slice.call(arguments,1));//["b", "c"],slice(1)}test("a","b","c");

将函数的实际参数转换成数组的方法

方法一:var args = Array.prototype.slice.call(arguments);

方法二:var args = [].slice.call(arguments, 0);

方法三:

var args = []; for (var i = 1; i < arguments.length; i++) {     args.push(arguments[i]);}

转数组通用函数

var toArray = function(s){    try{        return Array.prototype.slice.call(s);    } catch(e){        var arr = [];        for(var i = 0,len = s.length; i < len; i++){            //arr.push(s[i]);               arr[i] = s[i];  //据说这样比push快        }         return arr;    }}
posted @ 2021-11-30 20:15  Shirooil  阅读(43)  评论(0)    收藏  举报