JavaScript入门(学习笔记)

本文详细介绍了JavaScript的历史、语法、数据类型、运算符、流程控制、面向对象、函数、DOM操作等内容,适合初学者入门。从变量声明、字符串、数值型、布尔型、对象到函数创建和调用,再到DOM查询、事件处理,覆盖了JavaScript的基础知识点。此外,还讨论了JavaScript的内存结构、类型转换、垃圾回收机制,以及浏览器隐含参数arguments对象。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考视频

注:本博客代码内容是在2020版webstorm编写测试

历史

起源:JavaScript诞生于1995年,它的出现主要是用于处理网页中的前端验证。在带宽比较低时,如果表单验证交给后端服务器来校验会影响效率,所以出现了JavaScript来前端验证。

简史:网景公司开发了liveScript,Sun公司介入,改名为JavaScript。->1996年,微软发布JScript。后来,网景公司将JavaScript开源。->几大公司联合发布ECMAScript作为JS的标准。

关系:ECMAScript(ES)是标准,JavaScript(JS)是根据标准的具体实现。JS实现由ES、DOM(文档对象模型)、BOM(浏览器对象模型)三部分组成。

特点:1.解释型语言;2.类似C和Java的语法结构;3.动态语言;4.基于原型的语言。

HelloWorld

JS代码需要编写到script标签中可以在写JS代码前写注释

<!--JS代码需要编写到script标签中-->
<script type="text/javascript">
    /** 控制浏览器弹出一个警告框 **/
    alert("Hello World!");
    /* 在页面中输出一个内容 */
    /* document.write()向文档中写内容。网页查看源代码时,body标签中没有内容,但是查看内存(F12查看元素)body中有内容 */
    /* console.log()向控制台输出内容。可以通过F12查看console看到*/
    document.write("Hello World");
    console.log("Hello World!");
</script>

JS代码编写的位置

  • 可以在HTML的head标签或者body标签的script标签中直接编写代码。

  • 可以写在onclick 或者超链接的href属性中(onclick和href写法有差别,href需要写上javascript:) 结构与行为耦合,不推荐

<button onclick = "alert('Hello world');">按钮</button>
<a href = "javascript:alert('hello world');">Hyperlink</a>
<a href = "javascript:;">Hyperlink</a>   /*常用于点击超链接没反应*/
  • 可以将JS写在外边JS文件中,通过script标签的src属性引入HTML文件。

    解耦,一处编写多处使用。可以利用浏览器的缓存机制

    一个script标签中一旦用了引用外部JS,在此标签中不能再写内部JS。如果要有,则可以用多个script标签

    <!--外部JS文件-->
      document.write("外部JS文件Hello World");
    
    <!--HTML文件-->
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>HelloWorld</title>
    </head>
    <body>
    <script  type="text/javascript" src="HelloWorld.js">
    </script>
    </body>
    </html>
    

JS语法

注释

/*
多行注释
*/

// 单行注释

符号

字面量和变量

字面量:不可改变的量或值,可直接使用。

变量:用于保存字面量。

变量声明

使用关键字var来声明一个变量

var a; //声明。如果没声明报错XXX is not defined
a = 100; //赋值。如果没赋值报错undifined
console.log(a);

var a = 100;//也可以声明和赋值同时进行。
var a = 1,b = 2;//多变量同时声明和赋值

标识符

  • 规则与其他编程语言差不多,但是可以有$符。

    JS底层采用Unicode编码保存标识符,理论上可以是中文

数据类型

JS有5种基本数据类型:字符串型(String)、数值型(Number)、布尔型(Boolean)、Null型(Null)和Undefined型(Undefined),1种引用数据类型:对象(Object)。

使用typeof运算符检查数据类型。

字符串

  • 单引号或双引号包裹

  • 使用\来转义字符:\n 换行;\t 制表符;\u Unicode编码

  • EcmaScript 6中支持 反引号``包裹字符串,成为模版字符串,换行和字符串拼接很方便。

  • ``号中,与变量的拼接不需要用+变量+来进行,只需要${变量名}

console.log("\u1C00");//输出Unicode编码1C00的内容

var str= `
hello
world
`;

数值型

  • 包括整数和浮点数
  • 特殊的数字:Infinity 正无穷;NaN 非法数值(Not A Number)
  • 进制表示前缀:0b 二进制;0 八进制【2020版webstorm是0o】;0x 十六进制

布尔型

  • true 或 false

Null型

  • 专门用来表示为空的对象,只有一个Null值
  • typeof进行类型检查时,返回object

Undefined型

  • 变量仅声明未赋值则为undefined
  • typeof类型检查时,返回undefined

Object型

除了基本数据类型以外,所有都是对象。属于复合的数据类型,类似与C语言中的结构体。

基本数据类型和引用数据类型内存结构

  • JS中的变量都是保存到栈内存中的;
  • 基本数据类型的值直接在栈内存中存储,值与值之间是独立存在,修改一个变量不会影响其他的变量;
  • 对象是保存到堆内存中的,每创建一个新的对象,就会在堆内存中开辟出一个新的空间,而变量保存的是对象的内存地址(对象的引用),如果两个变量保存的是同一个对象引用,当一个通过一个变量修改属性时,另一个也会受到影响。

类型转换

转为String

  • 隐式转换:任意类型+’’
  • 对象调用toString()方法。不改变原变量数据类型;不适用于null和undefined
  • 调用String()函数。对于Number Boolean String相当于调用toString()方法,null和undefined直接转化为字符串;不改变原变量的数据类型
var a = null;
a = a+''; //方式一

var b = true;
b.toString();//方式二,有缺陷

var c = undefined;
String(c);//方式三

转为Number(数值)

转换规则:null是0,undefined是NaN,true是1,false是0,String类型根据内容直接转化成数字或者NaN,空或纯空格字符串为0

  • 隐式转换:+ 可用于任意基本数据类型

  • 调用Number()函数。

  • 调用parseInt()或parseFloat()函数(一开始就是数值)

    提取字符串中有效数值内容

    parseInt()提取整数内容,parseFloat()提取含小数内容

    非字符串类型用parseInt()或parseFloat()会先自动转化为String类型参数传入函数

    第二个参数是进制

var a = null;
a = +a; //方式一
Number(a);//方式二

var b = "90.80px";
parseInt(b);//方式三,90
parseFloat(b);//90.8

var c = "11";
parseInt(c,2);//以二进制输入,得到十进制输出,3

转为Boolean

转换规则:空串、0、NaN、null、undefined,一次非运算后为true;其余为false

  • 隐式转换:!
  • 调用Boolean()函数转换规则同非运算
var a = '';
b = !a;//方式1,true
Boolean(a);//方式2,true

运算符

算数运算符

+ - * / %

  • 除加法以外,对非Number类型的值进行运算时,都会先转换为Number然后在做运算。
  • 加法运算时,遇到字符串时会发生拼串,跟java类似。

一元运算符

一元运算符只需要一个操作数

+:即正号,不会对值产生任何影响,但是可以将一个非数字转换为数字

-:即负号,可以对一个数字进行符号位取反

++:自增,a++或++a

–:自减,a–或--a

逻辑运算符

!:非运算

&&:与运算,短路与

||:或运算,短路或

赋值运算符

=;+=;-=;*=;/=;%=

关系运算符

>;>=;<;<=

  • 比较大小关系后返回布尔值;
  • 存在非数值(且不全是String类型)会先转换成Number型后比较
  • 如果两个都是String类型的会逐个比较字符的Unicode编码
  • 任何值和NaN比较,结果都为false
  • 比较字符串型的数字时,注意转型成数值,不然会按Unicode编码逐位比较

相等运算符

==:自动进行类型转换,将其转换为相同的类型后比较。

!=:自动进行类型转换,将其转换为相同的类型后比较。

===:全等。不自动类型转换,类型不同直接false

!==:不全等。不自动类型转换,类型不同直接true

PS:1.null != 0;2.undefined == null;3.NaN !=NaN

基本数据类型比较值大小,引用数据类型比较对象的地址。

判断变量是否为NaN,可以用isNaN()函数判断

条件运算符(三元运算符)

条件表达式?语句1:语句2;

如果该值为true,则执行语句1,并返回执行结果;否则执行语句2,并返回执行结果。

运算符优先级

可查看运算符优先级的表。

注:&&优先级高于||

typeof运算符

用来检查一个变量的数据类型,返回一个用于描述类型的字符串作为结果。

function Person(name){
    this.name = name;
}

var obj = new Object();
obj.name = 'Tom';
var person1 = new Person('Jerry');

console.log(typeof obj);//object
console.log(typeof person1);//object
console.log(obj);//{name:'Tom'}
console.log(person1);//Person {name:'Jerry'}

in 运算符

用来检查某属性是否在该对象中。对象中没有但原型中有也会返回true。与hasOwnProperty()方法区别。

var obj = new Object();
obj.name = 'Tom';

console.log('name' in obj);//true
console.log('age' in obj);//false

instanceof运算符

用来检查一个对象是否是一个类的实例,返回true或false。

代码块

在JS中可以使用{}来为语句进行分组,只具有分组的的作用,没有其他的用途,外部完全可见。

{
				var a = 10;	
				alert("hello");
				console.log("world");
				document.write("!");
}

流程控制

条件判断

var flag = true;
if(flag){
  document.write("flag is true.");
}

var flag = true;
if(!flag){
  console.log("flag is true.");
}else{
  alert("flag is flase.");
}

var num = 10;
if(num > 20 ){
  alert("num is bigger than 20.");
}else if(num > 10){
  console.log("num is between 10 and 20.");
}else{
  document.write("num is smaller than 10.");
}

条件分支

var num = '1';
switch(num){
    case 1:
        alert("开");
        break;
    case 2:
        alert("关");
        break;
    default:
        alert("wrong number.");
        break;
}//wrong number.

注:每条case里都有一个break,不然会已进入就按序执行直到遇见break或结束;字符串"1"不能匹配数字1,所以上述结果是"wrong number."

循环

//while
var num = 5;
while(num){
    console.log(num+'\n');
    num--;
}

//do...while
var flag = flase;
do{
  console.log("do...while...");
}while(flag)

//for
for(var i = 0; i < 5; i++){
  console.log("i:"+i);
}

break关键字用于退出最近的switch或循环语句

continue关键字用于跳过最近的当次循环

可以为循环语句创建一个label,来标识当前的循环。使用break和label结束指定循环。

outer:for(var i = 0;i < 5;i++ ){
    console.log("@外层循环"+i);
    inner:for(var j = 0;j < 5;j++ ){
        if(i > 3){
            break outer;
        }
        console.log("@内层循环"+j);
    }
}//i==3时,外循环停止,即整个循环结束。

注意事项

  • JS代码严格区分大小写
  • 以分号结尾(不写时浏览器会自动添加,但会消耗系统资源,有时浏览器会加错)
  • 忽略多个空格和换行

面向对象

对象的分类:

  • 1.内建对象,由ES标准中定义的对象,在任何的ES的实现中都可以使用。如:Math String Number Boolean Function Object…
  • 2.宿主对象,由JS的运行环境提供的对象,目前来讲主要指由浏览器提供的对象,如:BOM DOM
  • 3.自定义对象,由开发人员自己创建的对象。

对象创建

  • 使用new关键字调用构造函数(constructor),如:Object(), Function()等;
  • 使用对象字面量来创建,即{};【注意分号,代码块不需要,这里需要】
//方式1
var obj = new Object();

//方式2
var obj = {};

对象的属性

添加属性

  • 对象.属性名 = 属性值 注意这里属性名不能加引号

  • 对象[“属性名” ] = 属性值 注意这里属性名必须要加引号

    []方式优点:属性名可以传递变量;当属性名比较复杂时必须通过这种方式

  • 对象创建时通过键值对添加属性,多个键值对之间 逗号 隔开,最后一个不要逗号

    属性名可加引号也可不加,建议不加。如果特殊字符,必须加。

var obj = Object();
obj.name = "Tom";//方式1
obj["animal"] = "Cat";//方式2

var obj2 = {
  name:"Jerry",
  age:8,
  animal:"Rat"
};//方式3

PS:如果一个函数作为一个对象的属性保存,称此函数是此对象的方法。

var obj = new Object();
obj.name = 'Tom';
obj.age = 9;
obj.Info = function(){
  return obj.name+"is"+age;
};

obj.Info();//调用obj的Info方法

读取属性

var obj = Object();
obj.name = "Tom";
obj["animal"] = "Cat";

console.log(obj);//{name: "Tom", animal: "Cat"}
console.log(obj['name']);//Tom
console.log(obj.animal);//Cat
console.log(obj.gender);//没有的属性,结果显示undefined

修改属性

var obj = Object();
obj.name = "Tom";
obj["animal"] = "Cat";

obj['name'] = "Jerry";
obj.animal = "Rat";

删除属性

var obj = Object();
obj.name = "Tom";

delete obj.name;//删除属性

遍历对象属性

类似python的字典遍历。

var obj = Object();
obj.name = "Tom";
obj["animal"] = "Cat";

for (var key in obj ){
  console.log(key + ":" + obj[key]);
}

函数

函数也是一个对象。

函数创建

  • 使用 构造函数 创建函数对象(很少用):需要封装的代码以字符串参数传递;
  • 使用 函数声明 创建函数
  • 使用 函数表达式 创建函数
//方式1
var fun1 = new Function("console.log('hello world 1.');");

//方式2
function fun2(num1,num2){
  var sum = num1 + num2;
  console.log(sum);
  return sum;
}

//方式3
var fun3 = function(){
  console.log("hello world 3.");
};

函数调用

调用函数时解析器不会检查实参的类型。有可能会接收到非法的参数,如果可以的话,最好对参数进行类型的检查。

调用函数解析器不会检查实参的数量。多余实参不会被赋值,如果实参的数量少于形参的数量,则没有对应实参的形参将是undefined。

实参可以是一个对象,如果参数过多,可以将参数封装到一个对象arguments中传递。

fun2(2,5);//函数名(实参);

函数返回值

return 返回值;

  • return语句后不跟任何值或不写return都返回一个undefined
  • return后可以跟任意类型的值

匿名函数立即执行

(function(a,b){
	console.log("a+b = "+(a+b);
})(3,4);

PS:把函数()包围,是为表示是一个整体,直接将整体当作函数句柄后调用。

this关键字

解析器在调用函数每次都会向函数内部传递进一个隐含的参数——this,this指向调用此函数或方法的对象,全局作用域调用函数时,this指向window对象。

改变this指向

call()和apply():通过函数对象来调用,传入参数作为this对象。

都是函数调用,但是函数对象如果用call()和apply(),可以强制改this指向。

call()和apply():第一个参数是传入的this对象,后面是实参。apply()实参需要以数组形式传入。

function f(a,b){
    console.log(this);
    console.log('a='+a);
    console.log('b='+b);
}

var obj = {
    name:'obj'
};

f(1,2);//Window {...} a=1 b=2
f.call(obj,3,4);//{name: "obj"} a=3 b=4
f.apply(obj,[5,6]);//{name: "obj"} a=5 b=6

作用域

JS中有两种作用域:全局作用域和局部作用域。

作用域分类

全局作用域:

  • 直接编写在script标签中的JS代码,都在全局作用域

  • 全局作用域在页面打开时创建,在页面关闭时销毁

  • 在全局作用域中有一个全局对象window,代表一个浏览器的窗口,由浏览器创建可直接使用

    全局作用域的变量作为window对象的属性保存;函数作为window对象的方法保存

    全局作用域中的变量是全局变量,在页面的任意的部分都可以访问的到

var a = 10;
window.a = 20;//window对象调用a属性
console.log(a);//20

function fun(){
  console.log("fun函数。");
}
window.fun();//window对象调用fun()方法

PS:window对象调用属性与直接使用变量的区别是,如果该变量没有声明,window调用不报错,结果为undefined;直接使用则报错"变量未定义"。

函数作用域:

  • 调用函数时创建函数作用域,函数执行完毕以后,函数作用域销毁
  • 每调用一次函数就会创建一个新的函数作用域,互相独立
  • 在函数作用域中可以访问到全局作用域的变量,在全局作用域中无法访问到函数作用域的变量
  • 当在函数作用域操作一个变量时,它会先在自身作用域中寻找,如果有就直接使用,如果没有则向上一级作用域中寻找,直到找到全局作用域。
  • 在函数中要访问全局变量可以使用window对象
  • 函数中变量没有使用var关键字声明,则会成为全局变量,但声明没有提前,如果在变量定义前使用则报错。
  • 函数的形参也是在函数作用域
function fun(){
  a = 10;
  var b = 20;
  
  console.log("a="+a);
  console.log("b="+b);
}

fun();//a=10 b=20
console.log("a="+a); //a=10
console.log("b="+b);//报错

声明提前

在全局作用域或函数作用域中都有声明提前的特性。

变量声明提前

使用var关键字声明的变量,会在所在作用域的代码执行前声明(但不会赋值),不使用var关键字声明,则不会被声明提前。

//使用var,代码语句先使用后声明,不报错,但结果为undefined
console.log(a);
var a = 12;

//不使用var,代码语句先使用后声明,报错
console.log(a);//ReferenceError: Can't find variable: a
a = 12;

函数声明提前

使用 函数声明 形式创建的函数 function 函数(){},会在所在作用域的代码执行前创建,可在函数声明前来调用。

其他方式(使用 Function()构造函数 或 使用 函数表达式 )【有var】创建则不能提前。【因为此时虽然函数对象的变量通过var声明提前了,但值为undefined,所以不能调用】

// 使用函数声明,没有var,可直接执行
fun1();
function fun1(){
  console.log("我是fun1函数");
}

//使用函数表达式创建,有var,报错
fun2();
var fun2 = function(){
  console.log("我是fun2函数");
}
//使用Function()创建,有var,报错
fun3();
var fun3 = new Function("console.log('我是fun3函数');");

创建对象

工厂方法创建对象

此方法创建的对象使用的构造函数是Object,所以对象的类型都是object类型。

function createPerson(name ,age){
  //1.创建一个新的对象 
	var obj = new Object();
	//2.向对象中添加属性
	obj.name = name;
	obj.age = age;
  //2.1 可以添加方法
  obj.sayName = function(){
    console.log(this.name);
  }
  //3.将新对象返回
  return obj;
}

var person1 = createPerson('Tom',8);

构造函数创建对象1

避免了工厂方法创建对象都是object类型。

构造函数与普通函数

  • 创建方式相同,构造函数名称习惯首字母大写
  • 调用方式不同,普通函数直接调用,构造函数用new关键字调用
  • 返回值不同,普通函数需要return关键字返回内容,构造函数自动返回新建对象

构造函数的执行流程:

  • 1.立刻创建一个新的对象
  • 2.将新建的对象设置为函数中this,在构造函数中可以使用this来引用新建的对象
  • 3.逐行执行函数中的代码
  • 4.将新建的对象作为返回值返回

使用同一个构造函数创建的对象,我们称为一类对象,也将一个构造函数称为一个类。通过一个构造函数创建的对象,称为是该类的实例。

可以类比Java的类、构造器、实例。JS的构造函数有点向Java类和构造器的混合模式。

function Person(name,age){
  this.name = name;
  this.age = age;
  this.sayName = function(){
    console.log(this.name);
  };
}

var person1 = new Person('Jerry',8);

构造函数创建对象2

上一个方式中每个对象的每一属性都是独立的,其中的方法也是,这样就造成了资源浪费。

function Person1(name, age){
  this.name = name;
  this.age = age;
  this.sayName = function(){
    console.log('Person1'+this.name);
  };
}

var person1 = new Person1('p11',5);
var person2 = new Person1('p12',6);
console.log(person1.sayName == person2.sayName); //false,说明两个对象的方法地址不同

改进1

将对象的方法放到在构造函数外定义。

function Person2(name, age){
  this.name = name;
  this.age = age;
  this.sayName = fun;
}
function fun(){
  console.log('Person2' + this.name);
}

var per1 = new Person2('p21',5);
var per2 = new Person2('p22',6);
console.log(per1.sayName == per2.sayName);//true,说明两个对象的方法地址相同

改进2

改进1虽然可以节约资源了,但是因为构造函数的方法在外部作为全局函数定义,污染了全局作用域的命名空间(指对象的方法像全局函数,全局函数不能与该方法同名),也不安全。

改进2,向原型中添加该方法。有点像Java的static关键字,使属性和方法成为类属性和类方法。

function Person3(name){
  this.name = name;
}
Person3.prototype.sayName = function(){
  console.log('Person3'+this.name);
};

var p1 = new Person3('p31');
var p2 = new Person3('p32');
console.log(p1.sayName == p2.sayName);//true,说明两个对象的方法地址相同 

内置对象

数组(Array)

数组的存储性能比普通对象要好,可以直接通过索引来获取元素。

创建

  • 使用 构造函数Array() 创建
  • 使用[]字面量创建
var arr1 = new Array();//方式1
var arr3 = new Array(1,'a',3,null,5);//创建时初始化元素

var arr2 = [];//方式2
var arr4 = [1,true,3,undefined];//创建时初始化元素

销毁

arr = null;

查询

console.log(arr[3]);

增加/修改

同一数组可以不同类型,可以跳着赋值,未赋值的中间索引的元素值为undefined。

arr[0] = 10;
arr[2] = 'h';

arr(arr.length) = true;//向数组末尾添加元素

也可通过方法:push()unshift()splice()

删除元素

delete a[0];

也可通过方法:pop()shift()splice()

内建属性

length

获取数组长度【无论连续还是非连续数组,length都是 最大索引值+1】

console.log(arr.length);

修改length

如果修改的length大于原长度,则多出部分会空出来;如果修改的length小于原长度,则多出的元素会被删除。

arr.length = 10;

内建方法

push()

向数组的末尾添加一个或多个元素,并返回数组的新的长度。

var arr = ['a',2,3,true];

var newLength = arr.push('b',undefined);
console.log(arr[4]);//b
console.log(newLength);//6
pop()

删除数组的最后一个元素,并将被删除的元素作为返回值返回。

var arr = ['a',2,3,true];
console.log(arr.pop());//true是删掉的元素
unshift()

向数组开头添加一个或多个元素,并返回新的数组长度。后面元素索引自动调整。

var arr = ['a',2,3,true];
var newLength = arr.unshift('b',undefined);

console.log(newLength);//6
console.log(arr);//["b", undefined, "a", 2, 3, true] (6)
shift()

把数组开头元素删除,并返回删除元素。

var arr = ['a',2,3,true];

console.log(arr.shift());//a
console.log(arr);//[2, 3, true] (3)
forEach()

遍历数组元素,需要一个函数作为参数。函数声明如下:

forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void;

callbackfn回调函数:由我们创建,系统自动调用。

使用见遍历方式3

slice()

用来从数组提取指定元素,不改变元素数组,而是将截取到的元素封装到一个新数组中返回。

//函数声明
slice(start?: number, end?: number): T[];
//开始索引(含)~结尾索引(不含,可忽略),返回数组T[]
//索引可以传递负数,负数表示从后往前数,从-1开始

//使用
var arr = ['a',2,3,true];
delete arr[2];

var result = arr.slice(1,3);
console.log(result);//[2] (2),因为索引2的元素已经被删除,所以不显示,但得到的数组长度还是为2
splice()

用于删除数组中的指定元素,会影响到原数组。将指定元素从原数组中删除后,被删除的元素作为返回值返回。

//函数声明
splice(start: number, deleteCount: number, ...items: T[]): T[];
//start 开始删除的索引,deleteCount 删除的数量,...items 从删除位置的新增元素
//当deleteCount为0时,可用于新增元素

//使用
var arr = [0,1,2,3,4,5,6];
// delete arr[2];

var result = arr.splice(1,1,'7','8');
console.log(arr);//[0, "7", "8", 2, 3, 4, 5, 6] (8)
console.log(result);//[1] (1)
concat()

连接两个或多个数组,返回值为新的数组,原数组不变。

var arr = [1,2,3];
var arr2 = ['4','5','6'];
var arr3 = [true,false];
var new_arr = arr.concat(arr2,arr3);
console.log(new_arr);//[1, 2, 3, "4", "5", "6", true, false] (8)
join()

将数组转换为一个字符串,不会对原数组产生影响,而是将转换后的字符串作为结果返回。

可以指定一个字符串参数作为连接符。

var arr = [0,1,2,3];
var str = arr.join("#");
console.log(str);//0#1#2#3
reverse()

反转数组(元素按索引位置首尾对应一一,交换),直接修改原数组

sort()

对数组中的元素进行排序。会影响原数组,默认会按照Unicode编码进行默认升序排序。

**PS:对于纯数字数组也会按Unicode编码来排序,所以有可能得到错误结果(编码 11 < 2)。**可以通过添加回调函数指定排序规则来解决。

var arr = [5,4,2,1,3,6,3];
arr.sort(function(a,b){
    return a-b;//升序,b-a降序。
});

PPS:arr中数据两两传入回调函数,不同浏览器规则不同,传入顺序也可能不同。与视频讲解有出入。

数组遍历

var arr = ['a',2,3,true];

//方式1
for(var i in arr){
  console.log(i+':'+arr[i]);
}//遍历所有元素,空索引跳过

//方式2
for(var i = 0 ; i < arr.length ;i++){
  console.log(arr[i]);
}//遍历所有索引的值,空索引不跳过

//方式3
arr.forEach(function(value , index , obj){
        console.log(value);
    });//遍历所有元素,空索引跳过 

Date对象

在JS中使用Date对象来表示一个时间。

Date对象创建

var d = new Date();
console.log(d);//Tue Mar 02 2021 21:41:45 GMT+0800 (CST) 直接返回时间

//指定时间,传递字符串,格式月份/日/年 时:分:秒
var d2 = new Date("2/18/2021 11:10:30");
console.log(d2);//Thu Feb 18 2021 11:10:30 GMT+0800 (CST)

Date对象方法

获取指定时间信息

getDate():获取当前日期对象是几日。d.getDate()

getDay():获取当前日期对象时周几。0-6,从周日开始

getMonth():获取当前时间对象的月份。0-11,从一月开始

getFullYear():获取当前日期对象的年份。

getTime():获取当前日期对象的时间戳。从格林威治标准时间的1970年1月1日,0时0分0秒到当前日期所花费的毫秒数(1秒 = 1000毫秒)。

    var d = new Date("1/1/1970 0:0:0");
    time = d.getTime();
    console.log(time);//-28800000,目前系统在东八区,以东八区为0,则结果为-8h

Eval()

eval()函数会将传入的字符串当做 JavaScript 代码进行执行。

var num = eval("2+2");
console.log(num);//4
console.log(typeof(num));//number

var obj = eval(new String("2+2"));
console.log(obj);//[String: '2+2']

eval()可将特定字符串内容转换成JSON格式

var str = `
    {
        "name": "LiLi",
        "age": 22,
        "sex": "F"
    },
    {
        "name": "LaLa",
        "age": 18,
        "sex": "F"
    }
`;
var json = eval('[' + str + ']');
//为了避免将其当成{}代码块运行,需要在{}外包裹符号,例如()。
//但又因为里面有两个{},为了避免','运算符的运算,在{}包裹[],使之成为两个json对象的数组

eval() 是一个危险的函数, 它使用与调用者相同的权限执行代码。如果eval() 运行的字符串代码被恶意方(不怀好意的人)修改,最终可能会在网页/扩展程序的权限下,在用户计算机上运行恶意代码。而且,运行性能较低(慢)。可以用Function替代。

使用Function构造一个函数,将字符串作为程序代码参数传入。

var json2 = (new Function('jsObj=['+str+'];return jsObj'))();
console.log(json2);
  • new Function(‘jsObj=[’+str+’];return jsObj’),构造了一个函数,函数体内容为

    jsObj=[{
        "name": "LiLi",
        "age": 22,
        "sex": "F"
    },
    {
        "name": "LaLa",
        "age": 18,
        "sex": "F"
    }];
    return jsObj
    
  • (new Function(‘jsObj=[’+str+’];return jsObj’))将整个函数包裹,得到函数句柄**【原本 var fun1 = new Function(); 函数是有名的,如果函数只调用一次,为省命名空间或存储资源,用()包裹部分可理解为匿名函数的名】**

  • (new Function(‘jsObj=[’+str+’];return jsObj’))()调用函数

Function

每个 JavaScript 函数实际上都是一个 Function 对象。运行 (function(){}).constructor === Function // true 便可以得到这个结论。

new Function ([arg1[, arg2,...argN],]functionBody)
//[arg1[, arg2,...argN] 参数,逗号分隔
//functionBody:一个含有包括函数定义的 JavaScript 语句的字符串

JS知识点

原型

每一个函数,解析器都会向函数中添加一个属性prototype,该属性指向一个对象,即原型对象。

如果函数作为普通函数调用prototype没有任何作用,当函数以构造函数的形式调用时,它所创建的对象中都会有一个隐含的属性,指向该构造函数的原型对象,可以通过__proto__来访问该属性【前后各两个下划线】。

原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中。

访问顺序:访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用,如果没有则会去原型对象中寻找,如果找到则直接使用。

用途:创建构造函数时,可以将这些对象共有的属性和方法,统一添加到构造函数的原型对象中。优点:1.不用每个对象的共有属性或方法独立,既浪费空间,也不方便统一修改;2.放到原型属性中,不会影响到全局作用域。

function P(){
  console.log("Hello");
}
P.prototype.name = 'Tom';//向原型中添加属性
P.prototype.sayHei = function(){
  console.log("Hei")
};//向原型中添加方法

var p = new P();//Hello
console.log(p.__proto__);//P {name: "Tom", sayHei: function}

PS:有点类似Java的static。共有的属性和方法——Java 中的类属性和类方法。不同的是,Java父类中的类方法和子类的同名方法不构成重写,但是JS的原型中的方法与子类的同名方法算重写。

原型链

原型对象也是对象,它也有原型。

当我们使用一个对象的属性或方法时,访问顺序:

  • 1.自身中寻找;
  • 2.自身中没有,则去原型对象中寻找;
  • 3.原型对象中没有,则去原型对象的原型中寻找
  • 。。。
  • 直到找到Object对象的原型对象,停止。
  • 从而形成了一条原型链。

Object对象的原型没有原型【一层原型,object原型对象的__proto__返回Null】,如果在Object原型中依然没有找到,则返回undefined

function Person(){
  
}
Person.prototype.name = 'Tom';

var p1 = new Person();
console.log(p1.__proto__.__proto__.__proto__);//null,第一层proto是p1对象所属类Person类的属性,第二层proto是Person类所属父类Object类的属性,因为Object是所有类的父类,所以第三层proto没有指向,值为null。

垃圾回收

垃圾:当一个对象没有任何的变量或属性对它进行引用,此时我们将永远无法操作该对象,这种对象就是垃圾。垃圾过多会占用内存空间,导致程序运行变慢。

JS中拥有自动的垃圾回收机制,会自动将这些垃圾对象从内存中销毁,我们不需要也不能进行垃圾回收的操作。我们能做的是将不用的对象设置为null

浏览器隐含参数

arguments对象

是类数组对象(不是数组对象),它也可以通过索引来操作数据,也可以获取长度。

  • 作用:保存实参,实参超过形参的部分也可通过arguments[索引]获取
  • 属性:callee,指向当前的函数对象
function fun(a,b){
  console.log(arguments[0]);//hello
  console.log(arguments[1]);//world
  console.log(arguments.callee == fun);//true
}
fun('hello','world');

在调用函数时,浏览器每次都会传递进两个隐含的参数:this和arguments

包装类

在JS中提供了三个包装类,通过这三个包装类可以将基本数据类型的数据转换为对象。String() Number() Boolean()

一般在实际应用中不会使用基本数据类型的对象,如果使用基本数据类型的对象,在做一些比较时可能会带来一些不可预期的结果,如:false转换成Boolean对象,用if判断为true。

方法和属性之能添加给对象,不能添加给基本数据类型。当一些基本数据类型的值去调用属性和方法时,浏览器会临时使用包装类将其转换为对象,然后再调用对象的属性和方法,调用完成后,会立马转回基本数据类型。

字符串属性/方法

字符串在底层是以字符数组的形式保存的,但不能等同于数组,有些数组方法不能直接用,需要split("")拆分成字符数组,然后用完数组方法,在用join("")又转换成字符串。例如要使用reverse()方法:

var str = prompt('Plz input any words:');//输入hello world!
str = str.split('').reverse().join('');//!dlrow olleh

属性

length属性

获取字符串的长度

对象方法

str = 'abcd';

charAt()

返回字符串中指定索引位置的字符。从0开始。

str.charAt(0);//a,str = 'abcd'

charCodeAt()

获取指定位置字符的字符编码(Unicode编码)。a:97,A:65,0:48。

str.charCodeAt(0);//97,str = 'abcd'

concat()

用来连接两个或多个字符串,作用与+一致。

str.concat("Hello","World");//abcdHelloWorld,str='abcd'时

indexof()

检索一个字符串中是否含有指定内容,含有,则返回其第一次出现的索引;否,则返回-1。

第二个参数是指定开始查找的位置(含)。

//函数声明
indexOf(searchString: string, position?: number): number;
//实例
var str = 'abcdbbb';
console.log(str.indexOf('b',2));//4
console.log(str.lastIndexOf('b',5));//5

lastIndexOf()

类似indexof(),从后往前找。

var str = 'abcdbbb';
console.log(str.lastIndexOf('b',5));//5

slice()

从字符串中截取指定索引的内容。可以是负数,可以省略第二个参数。

str.slice(1,3);//bc

substring()

作用类似slice(),参数不能为负(负数默认为0),自动调整参数使第一个参数小于第二个参数。

str.substring(2,-1);//ab
str.slice(2,-1);//c

substr()

按长度截取字符串。参数1:其实索引,参数2:截取长度

str.substr(1,2);//bc

split()

可以将一个字符串拆分为一个数组。如果传入空串,则每个字符都拆开。也可传入正则表达式【不加引号】。

str.split('b');//['a','cd]
var result = str.split(/[A-z]/);//传入该正则表达式,安照任意字母拆分字符串

search()

搜索字符串中是否含有指定内容,搜索到指定内容,则会返回第一次出现的索引,如果没有搜索到返回-1。也可传入正则表达式【不加引号】。

serach()只会查找第一个,即使正则表达式设置全局匹配也没用

str = "hello abc hello aec afc";
result = str.search(/a[bef]c/);//6,搜索字符串中是否含有abc 或 aec 或 afc

match()

从一个字符串中将符合条件的内容提取出来,参数可以是正则表达式【不加引号】。返回匹配到的内容数组。

正则表达式设置全局匹配会匹配到所有的内容,默认匹配到第一个就停止。

str = "1adf2ab36f7A8B9C";
result = str.match(/[a-z]/ig);//["a", "d", "f", "a", "b", "f", "A", "B", "C"] (9)

replace()

将字符串中指定内容替换为新的内容,参数可以是正则表达式。返回替换后的字符串。

正则表达式设置全局匹配会替换所有符合的内容,默认只替换第一个就停止。

str = "1adf2ab36f7A8B9C";
result = str.replace(/[a-z]/gi , "@");//1@@@2@@36@7@8@9@
result2 = str.replace(/[a-z]/gi , "");//1236789,用于删除某些元素

toUpperCase() toLowerCase()

toUpperCase():将一个字符串转换为大写并返回

toLowerCase():将一个字符串转换为小写并返回

str = str.toUpperCase();//ABCD
str = str.toLowerCase();//abcd

类方法

formCharCode()

根据字符编码去获取字符,用String类去调用。String.fromCharCode(0x61);//a,0x61=97(十进制)

DOM(Document Object Model)

DOM 文档对象模型。

  • 文档:表示整个HTML网页文档;
  • 对象:表示将网页中的每一个部分都转换为了一个对象;
  • 模型:使用模型来表示对象之间的关系,方便获取对象。把文档、各级标签、文本内容提取成节点构成模型,形似树,成为DOM树。

JS通过DOM来对HTML文档进行操作,修改WEB页面。

节点

节点Node是HTML文档最基本的单元。

常用的节点

有四类:

  • 文档节点:整个HTML文档;
  • 元素节点:HTML文档中的HTML标签;
  • 属性节点:元素的属性;
  • 文本节点:HTML标签中的文本内容。

节点的属性

节点/属性nodeNamenodeTypenodeValue
文档节点#document9null
元素节点标签名1null
属性节点属性名2属性值
文本节点#text3文本内容

文档节点(Document)

document对象是window对象的属性,不用获取可以直接使用。

通过document对象可以在整个文档访问内查找节点对象,创建各种节点对象。

元素节点(Element)

HTML中的各种标签都是元素节点。

可以通过document对象的方法来获取元素节点:

  • getElementById():通过元素(标签)的id属性值来获取元素节点对象;
<body>
	<button id="btn" onclick="alert('haha!')">按钮</button>
	<script type="text/javascript">
    	var btn = document.getElementById('btn');//通过id属性获取元素节点对象,标签对象script代码前
    	console.log(btn);//<button id="btn" οnclick="alert('haha!')">按钮</button>
    	btn.innerHTML = 'hehe';//修改btn对象的文字,或用innerText属性
	</script>
</body>

事件

事件就是用户和浏览器之间的交互行为,比如:点击按钮,鼠标移动、关闭窗口等等。

名称触发条件
onclick单击
ondblclick双击
onload用户进入页面,页面全部加载完成时
onunload用户离开页面
onchange改变字段内容时
onmouseover鼠标移至HTML元素上方
onmouseout鼠标移出HTML元素上方
onmousedown鼠标点时
onmouseup鼠标抬起时
onfocus获得焦点时(常用于文本输入框等)
onscroll滚动条滑动时(W3School没找到)
onmousemove鼠标移动时触发

我们可以在标签的属性里设置js代码,事件触发代码执行,或者从事件处理器调用一个函数(需要传入事件对象this)。但这样结构和行为耦合,不便维护。

改进,为对象的对应事件绑定处理函数的形式来响应事件,事件触发时,对应的函数将会被调用。

<!--方式1&2代码写到了标签里-->
  
<!--方式1-->
<button id="btn1" onclick="this.innerText = '按钮1';">button1</button>

<!--方式2-->
<button id="btn2" onmouseover='changeText(this)'>button2</button>
<script type="text/javascript">
  var changeText = function(x){
    x.innerText = '按钮2';
  };
  </script>

<!--方式3-->
<button id="btn3">button3</button>
<script type="text/javascript">
    var btn2 = document.getElementById('btn3');
    btn2.ondblclick = function(){
        btn2.innerText = '按钮3';
    };//这里写到了对象.属性的函数里
</script>

事件对象

当事件的响应函数被触发时,浏览器每次都会将一个事件对象作为实参传递进响应函数,在事件对象中封装了当前事件相关的一切信息,比如:鼠标的坐标 等,可以在W3School中DOM event的属性中查看事件对象包含的信息。

在IE8及以下,响应函数被触发时,浏览器不会传递事件对象,而是将事件对象作为window对象的属性保存,通过window.event获取

IE8及以上,通过arguments[0]获取,或者在function中声明形参event来承接。

//解决事件对象兼容
XXX.onclick = function(){
  event = arguments[0] || window.event;//要放在对应的触发函数里,代表触发单击函数对象事件
}

事件的冒泡(Bubble)

所谓的冒泡指的就是事件的向上传导,当后代元素上的事件被触发时,其祖先元素满足其事件条件时也会被触发。如果取消可以在后代元素响应函数中添加event.cancelBubble = true;将冒泡取消。

文档加载

浏览器在加载一个页面时,是按照自上向下的顺序加载的,JS代码直接写在所需对象前,代码执行时,页面还没有加载,页面没有加载DOM对象也没有加载,会导致无法获取到DOM对象。建议写后面,如果要写前面,则需要为页面绑定onload事件,使页面加载完成后执行JS代码。

<head>
  <script type = 'text/javascript'>
    window.onload = function(){
    var btn = document.getElementById('btn');
    btn.onclick = function(){
      alert("hello~");
    };
  };
    </script>
  </head>
<body>
   <button id = 'btn'>I'm button<btn>
 </body>

DOM查询

W3School手册查找,首页HTML DOM->DOM方法

<body>
		<div id="total">
			<div class="inner">
				<p>
					你喜欢哪个城市?
				</p>

				<ul id="city">
					<li id="bj">北京</li>
					<li>上海</li>
					<li>东京</li>
					<li>首尔</li>
				</ul>
        
				<p>
					你喜欢哪款单机游戏?
				</p>

				<ul id="game">
					<li id="rl">红警</li>
					<li>实况</li>
					<li>极品飞车</li>
					<li>魔兽</li>
				</ul>
        
				<p>
					你手机的操作系统是?
				</p>

				<ul id="phone"><li>IOS</li><li id="android">Android</li><li>Windows Phone</li></ul>
			</div>

			<div class="inner">
				gender:
				<input type="radio" name="gender" value="male"/>
				Male
				<input type="radio" name="gender" value="female"/>
				Female
				<br>
				name:
				<input type="text" name="name" id="username" value="abcde"/>
			</div>
		</div>
		<div id="btnList">
			<div><button id="btn01">查找#bj节点</button></div>
			<div><button id="btn02">查找所有li节点</button></div>
			<div><button id="btn03">查找name=gender的所有节点</button></div>
			<div><button id="btn04">查找#city下所有li节点</button></div>
			<div><button id="btn05">返回#city的所有子节点</button></div>
			<div><button id="btn06">返回#phone的第一个子节点</button></div>
			<div><button id="btn07">返回#bj的父节点</button></div>
			<div><button id="btn08">返回#android的前一个兄弟节点</button></div>
			<div><button id="btn09">返回#username的value属性值</button></div>
			<div><button id="btn10">设置#username的value属性值</button></div>
			<div><button id="btn11">返回#bj的文本值</button></div>
		</div>
	</body>

获取元素节点

通过document对象调用

getElementById():通过id属性获取一个元素节点对象

getElementsByTagName():通过标签名获取一组元素节点对象

getElementdByName():通过name属性获取一组元素节点对象

//getElementById(),查找#bj节点
var btn01 = getElementById('btn01');
var bj = getElementById('bj');
btn01.onclick = function(){
  alert(bj.innerHTML);
};

//getElementsByTagName(),查找所有li节点
var btn02 = getElementById('btn02');

btn02.onclick = function(){
  var lis = getElementsByTagName('li');
  var result_lis = [];
  for(var i=0;i<lis.length;i++){
    result_lis[i] = lis[i].innerText;
  }
  alert(result_lis);
};

//getElementdByName(),查找name=gender的所有节点
var btn03 = document.getElementById('btn03');

btn03.onclick = function(){
  var inputs = document.getElementByName('gender');
  var result_inputs = [];
  for (var i = 0 ;i < inputs.length; i++){
    result_inputs[i] = inputs[i].value;//了解input的属性,直接用.运算符
  }
  alert(result_inputs);
};

获取元素节点各关系节点

通过具体的元素节点调用。

getElementsByTagName():方法,返回当前节点的指定标签名后代节点

childNodes:属性,表示当前节点的所有子节点

firstChild:属性,表示当前节点的第一个子节点

lastChild:属性,表示当前节点的最后一个子节点。

parentNode:属性,表示当前节点的父节点

previousSibling:属性,表示当前节点的前一个兄弟节点

nextSibling:属性,表示当前节点的后一个兄弟节点

function btn_event(btn, fun) {
  this.btn = document.getElementById(btn);
  this.btn.onclick = fun;
}

//查找#city下所有li节点
var city = document.getElementById('city');
btn_event('btn04',fuction(){
          var city_lis = city.getElementsByTagName('li');
					for (var i = 0; i < city_lis.length; i++) {
            alert(city_lis[i].innerHTML);
          });
};

//返回#city的所有子节点
btn_event('btn05',function(){
  var city_nodes = city.childNodes;//所有节点,包含文本,以及节点标签元素间的空格或回车
  for (var i = 0; i < city_nodes.length; i++) {
        console.log(city_nodes[i]);
    }
    var city_children = city.children;
    for (var i = 0; i < city_children.length; i++) {
        alert(city_children[i]);
    }
});

//返回#phone的第一个子节点
btn_event('btn06',function(){
  var phone = document.getElementById('phone');
  alert(phone.firstChild);//phone.childNodes[0],含空白文本节点
  console.log(phone.firstElementChild);//第一个元素,IE8不支持
});

//返回#bj的父节点
btn_event('btn07',function(){
  var bj = document.getElementByIs('bj');
  var bj_parent = bj.parentNode;//父节点
  alert(bj_parent);
  console.log(bj.parentElement);
  console.log(bj_parent.innerText);//不含标签,只有文本
  console.log(bj_parent.innerHTML);//含标签,节点下的所有内容
});

//返回#android的前一个兄弟节点
btn_event('btn08', function () {
    var android = document.getElementById('android');
    alert(android.previousSibling);//前一个兄弟节点,含空白文本
    console.log(android.previousElementSibling);//前一个兄弟元素,不含空白文本,IE8及以下不支持
});

//读取#username的value属性值
var username = document.getElementById('username');
btn_event('btn09', function () {
    alert(username.value);
});

//设置#username的value属性值
btn_event('btn10', function () {
    var username_set = prompt('reset username');
    username.value = username_set;
});

//返回#bj的文本值
btn_event('btn11',function(){
    var bj = document.getElementById('bj');
    alert(bj.innerText);
    var bj_firchild = bj.firstChild;//bj的子节点是文本节点
    console.log(bj_firchild.nodeValue);//文本节点的nodeValue是其属性
});

获取元素节点属性

通过.运算符直接获取元素节点属性,例如:<input name='gender' value='male'/>,通过input对象.value得到’male’。

特殊:class属性不能直接通过 对象.class 获取,需要通过 对象.className 获取。

获取元素内部内容

.innerHTML :属性,元素节点通过该属性获取元素内部的HTML代码,自结束标签此属性无意义。

.innerText: 属性,用于获取元素内部文本内容

.nodeValue:文本节点属性,文本节点的节点属性可以直接改文本内容

其他查询方式

document.body:body标签对象

document.head:head标签对象

document.documentElement:HTML标签对象

通过CSS选择器查询

通过document对象调用CSS选择器来查询,传递一个选择器字符串作为参数,根据选择器内容查找元素。

querySelector():返回值为查找到的第一个元素

querySelectorAll():返回值为所有符合条件的元素,数组

var div = document.querySelector('.box1 div');//查找class为box1的第一个div
var box1 = document.querySelectorAll('.box1');//查找class为box1的所有对象,返回结果列表
var box2 = document.querySelectorAll('#box2');//查找id为box2的所有对象,返回结果列表(虽然id唯一,结果只有一个)

DOM增删改

<body>
	<div id="total">
		<div class="inner">
			<p>
				你喜欢哪个城市?
			</p>

			<ul id="city">
				<li id="bj">北京</li>
				<li>上海</li>
				<li>东京</li>
				<li>首尔</li>
			</ul>			
		</div>
	</div>
	<div id="btnList">
		<div><button id="btn01">创建一个"广州"节点,添加到#city下</button></div>
		<div><button id="btn02">"广州"节点插入到#bj前面</button></div>
		<div><button id="btn03">使用"广州"节点替换#bj节点</button></div>
		<div><button id="btn04">删除#bj节点</button></div>
		<div><button id="btn05">读取#city内的HTML代码</button></div>
		<div><button id="btn06">设置#bj内的HTML代码</button></div>
	</div>
</body>

增加元素/节点

document.createElement():创建一个元素节点对象,传入参数:标签名,返回值创建好的对象。

document.createTextNode():创建文本节点,传入参数:文本内容,返回值:文本节点。

父节点.appendChild():向父节点中添加一个新的子节点,传入参数,子节点对象。

find_btn('btn01',function(){
		//document.createElement(),可以用于创建一个元素节点对象,需要一个标签名作为参数,将会根据该标签名创建元素节点对象,并将创建好的对象作为返回值返回
		var li = document.createElement('li');
		//document.createTextNode(),可以用来创建一个文本节点对象,需要一个文本内容作为参数,将会根据该内容创建文本节点,并将新的节点返回
		var text_gz = document.createTextNode('广州');
		//appendChild(),向一个父节点中添加一个新的子节点,用法:父节点.appendChild(子节点);
		li.appendChild(text_gz);

		var city = document.getElementById('city');
		city.appendChild(li);
	});

插入元素/节点

父节点.insertBefore(新节点对象,旧节点对象):在指定子节点前插入新节点。

兄弟节点.insertAdjacentElement(参数1,插入节点):参数1可以是"beforebegin", “beforeend”, “afterbegin”, “afterend”,分别是“前标签开始”,“前标签最后”,“后标签的开始”,“后标签的最后”

  var li = document.createElement('li');
	li.id = 'gz';
	var gz_text = document.createTextNode('广州');
	li.appendChild(gz_text);
	var bj = document.getElementById('bj');
	var city = document.getElementById('city');
	// city.insertBefore(li,bj);
	// bj.insertAdjacentElement("beforebegin", li);//<li>广州</li> <li id="bj">北京</li>
	// bj.insertAdjacentElement("beforeend", li);//<li id="bj">北京<li>广州</li></li> <li>上海</li>
	// bj.insertAdjacentElement("afterbegin", li);//<li id="bj"><li>广州</li>北京</li> <li>上海</li>
	bj.insertAdjacentElement("afterend", li);//<li id="bj">北京</li> <li>广州</li> <li>上海</li>

元素/节点替换

父节点.replaceChild(新节点,旧节点):使用指定的子节点替换已有的子节点

旧节点.replace(新节点):使用指定的子节点替换已有的子节点

//使用"广州"节点替换#bj节点
find_btn('btn03',function(){
	var li = document.createElement('li');
	var gz_text = document.createTextNode('广州');
	li.appendChild(gz_text);

	var city = document.getElementById('city');
	var bj = document.getElementById('bj');
	// city.replaceChild(li,bj);
	bj.replaceWith(li);
});

删除节点/元素

父节点.removeChild():删除一个子节点。

不知父节点时:

​ 子节点.parentNode.removeChild(子节点);

​ 要删节点.remove()

find_btn('btn04',function(){
	var bj = document.getElementById('bj');
	var city = document.getElementById('city');
	// city.removeChild(bj);
	// bj.parentElement.removeChild(bj);//我调我父亲删我自己。。。
	bj.remove();
});

innerHTML增删改节点/元素

用innerHTML与节点方法调用增删改方法的区别是,innerHTML修改相当于将节点内容全部修改,对网页影响较大。一般还未添加到网页中前,用innerHTML方式修改,添加/影响网页中元素时,调用相应的方法。

myClick("btn07",function(){		
					//向city中添加广州
					var city = document.getElementById("city");
					//city.innerHTML += "<li>广州</li>";//动静较大
					
					var li = document.createElement("li");
					//向li中设置文本
					li.innerHTML = "广州";//未影响网页的li元素用innerHTML属性的方式修改
					//将li添加到city中
					city.appendChild(li);
				});

a标签点击会跳转,如果绑定了单击函数,希望不跳转,则将将单击函数返回值设为false,或者改变a标签的href属性为“javascript:;”

<table id="employeeTable">
		<tr>
			<th>Name</th>
			<th>Email</th>
			<th>Salary</th>
			<th>&nbsp;</th>
		</tr>
    <tr>
			<td>Tom</td>
			<td>tom@tom.com</td>
			<td>5000</td>
			<td><a href="deleteEmp?id=001">Delete</a></td>
		</tr>
</table>
var delete_a = document.getElementsByTagName('a');
for(var i = 0;i < delete_a.length;i++){
	delete_a[i].onclick = function(){
    var tr = this.parentNode.parentNode;//这里需要用this,因为循环绑定单击函数 在 单击函数响应 前,当点击时,i值已经跳出循环了,所以delete_a[i]的i不在索引范围内,不能用delete_a[i]代替this。
    var name = tr.children[0].innerHTML;
    var flag = confirm("确认删除"+name+"吗?");//弹出一个带有确认和取消按钮的提示框
    if(flag){
      //删除tr
      tr.parentNode.removeChild(tr);
    }
    return false;//取消超链接点击跳转的默认行为
  };
}

注意!:1.如果通过table标签增加信息时,其table下的所有内容会修改,那么最开始绑定的单击函数会失效;2.增加行时,还需要为新增的a标签绑定单击函数;3.table标签在网页中会自动增加标签包裹table标签下的内容。

DOM操作CSS

DOM操作内联样式

通过 对象.style.属性 读取/修改对象的内联样式。当遇到属性有-连接符时,用小驼峰命名法

  • 通过style修改的是内联样式,内联样式有较高的优先级,修改后页面会立即改变;
  • 如果在css样式中的属性写了!important,那么style修改的优先级略低,不能生效;
  • 宽度为auto或者未定义时,读取宽度时返回值为空(什么也没有)。
<head>
    <meta charset="UTF-8">
    <title>Tang</title>
		<!--css样式-->
    <style type="text/css">
        #box1{
            width:auto;
            height:200px;
            background-color:red;
        }
    </style>
    <script type="text/javascript">
        window.onload = function(){
            var btn01 = document.getElementById('btn01');
            btn01.onclick = function(){
                var box1 = document.getElementById('box1');
                box1.style.width = "100px";
              	box1.style.backgroundColor = 'yellow';
            };

            var btn02 = document.getElementById('btn02');
            btn02.onclick = function(){
                var box1 = document.getElementById('box1');
                alert(box1.style.backgroundColor);
            };
        };
    </script>
</head>
<body>
	<button id="btn01">btn01</button>
	<button id="btn02">btn02</button>
	<div id="box1"></div>
</body>

IE8: 元素对象.currentStyle.样式名 ,读取当前样式(包括CSS样式)。只能读取

IE8以上及其他浏览器:通过window的方法getComputedStyle()获取元素当前的样式对象。

  • 参数1:要获取样式的元素;
  • 参数2:传递一个伪元素(作用?),一般传null。
  • 只能读取样式,不能修改
  • 返回值为元素的样式对象,通过 返回值.样式名 读取特定样式
  • 宽度为atuo或者未定义时,读取宽度时,返回值为当前窗口元素宽度值
var btn02 = document.getElementById('btn02');
btn02.onclick = function(){
    var box1 = document.getElementById('box1');
    var obj_css = window.getComputedStyle(box1,null);
    console.log(obj_css.width);
};

浏览器兼容

自定义函数,将不同浏览器使用的同操作方法或函数包装。例如上述获取样式的方法。

function getStyle(obj , name){
	//用window.getComputedStyle代替直接的getComputedStyle,为了不报错,此处if-else简化为三元运算符
	if(window.getComputedStyle){
		//正常浏览器的方式,具有getComputedStyle()方法
		return getComputedStyle(obj , null)[name];
	}else{
		//IE8的方式,没有getComputedStyle()方法
		return obj.currentStyle[name];
	}
}

其他样式操作

通过W3School->Javascript->(JS参考手册)HTML DOM对象->(HTML DOM对象)DOM Element ,查阅元素/节点的属性和方法。

以下属性都只读不可改,除offsetParent都返回数值。

属性作用
clientWidth获取元素的可见宽度,含内容区和内边距
clientHeight获取元素的可见高度,含内容区和内边距
offsetWidth获取元素的整个的宽度,包括内容区、内边距和边框
offsetHeight获取元素的整个的高度,包括内容区、内边距和边框
offsetParent获取当前元素的定位父元素,就近原则,如果都没有则返回body
offsetLeft当前元素相对于其定位父元素的水平偏移量
offsetTop当前元素相对于其定位父元素的垂直偏移量
scrollWidth获取元素整个滚动区域的宽度
scrollHeight获取元素整个滚动区域的高度
scrollLeft获取水平滚动条滚动的距离
scrollTop获取垂直滚动条滚动的距离

练习1:设置内容必读后提交,具体描述:有一段较长内容,需要滚动条拖动阅读,在阅读未完成时,勾选和提交按钮才生效,提交后页面跳转到提交响应页面。

PS:scrollHeight - scrollTop == clientHeight 说明垂直滚动条滚动到底了


练习2:页面有两个框,第一个框用于鼠标移动范围框定,第一个框用于显示鼠标定位(x:?,y:?)

练习3:页面中有一个方块(div),当鼠标在任意位置移动时,方块跟随鼠标移动。

其他

编码

Unicode编码,是十六进制编号。在JS代码中如果需要使用需要加\u转义。

console.log("\u1C00");

在HTML标签内容中使用,需要加&#,且编号需要转换成十进制。

<h1>&#9760;</h1>

常用函数

测试计时console.time()和console.timeEnd()

console.time("name");
{
  测试代码段
}
console.timeEnd("name");//结果控制台输出name:XXXms

数学函数Math.xxx()

Math.PI;//表示的圆周率

Math.sqrt(3);//1.7320508075688772,对数开方,结果浮点数
Math.abs(-1);//计算一个数的绝对值
Math.ceil(3.1);//4,向上取整
Math.floor(3.1);//3,向下取整
Math.round(3.1);//3,进行四舍五入取整
Math.random();//生成一个0-1之间的随机数
Math.round(Math.random()*(y-x)+x)//x~y直接的随机数,可以用来生成x-y的随机数
Math.max(10,45,30,100);//100,获取多个数中的最大值
Math.min(10,45,30,100);//10,获取多个数中的最小值
Math.pow(3,2);//9,返回x的y次幂

输入函数prompt()

//函数声明
prompt(message?: string, _default?: string): string | null;
//输入函数,从键盘输入,参数可以是输入提示,返回值为string或null

常用方法

hasOwnProperty()

检查对象自身中是否含有该属性,只有自身含有该属性才返回true。与[in运算符](###in 运算符)区别。

var obj = new Object();
Object.prototype.name = 'ObjName';

console.log('name' in obj);//true
console.log(obj.hasOwnProperty('name'));//false

toString()

toString()方法一般返回[object Object],很多对象所属的类都重写了toString()方法,所以显示不同。

在页面中打印一个对象时,实际上是输出对象.toString()的返回值[教程与现有实践结果不同]

toString()可以指定表示进制

    function Person(name){
        this.name = name;
    }

    var per = new Person("Tom");

    per.toString = function(){
        return "hehe";
    };//toString在per.__proto__.__proto__中,现在相当于override

    console.log(per);//Person {name: "Tom", toString: function},按视频结果应该是“hehe”
    console.log("result:" + per);//result:hehe
    console.log(per.toString());//hehe

var num = 15;
console.log(num.toString(16));//f

Debug

Safari浏览器在页面右击“检查元素”出现导航条,选择“调试器”,在代码上添加断点。

可以通过在“监视表达式”新增要查看的变量或函数等,来看运行时它们的变化。

注意点

1:文字内容区设置横向和纵向滚动条

存在的问题,文字内容会自动换行,当内容过多时,父元素设置overflow:scroll;只出现纵向滚动条。

解决办法:子元素dispaly设为block,且取消自动换行white-space: nowrap;或word-break: keep-all;

#info {
    margin: 0px auto;
    width: 150px;
    height: 150px;
    overflow: scroll;
}
p {
    display: block;
    word-break: keep-all;/*或white-space: nowrap;*/
}

<div id="info">
    <p>1内容内容内容内容内容内容</p>
    <p>2内容内容内容内容内容内容</p>
    <p>3内容内容内容内容内容内容</p>
    <p>4内容内容内容内容内容内容</p>
    <p>5内容内容内容内容内容内容</p>
    <p>6内容内容内容内容内容内容</p>
    <p>7内容内容内容内容内容内容</p>
    <p>8内容内容内容内容内容内容</p>
</div>

2.代码格式

JS代码格式 standardjs可参考

3.()、[]、{}的使用

():放置函数的参数、调整运算优先级。

[]:1.表示定义一个数组,可理解为一个数组对象;2.用于数组读取元素

{}:1.表示定义一个对象,内含成对的属性和值。2.表示代码块

(){}:一般用于定义函数,例如function(形参){ 方法体 };

({}):一般用于指定对象,尤其是对象有多个子对象组成时,为了避免JS将{}解析成代码块,所以在外层加了()。

var obj = ({
        "name": "A",
        "age": 22,
        "sex": "F"
    }
        , {
        "name": "B",
        "age": 33,
        "sex": "F"
    }
);
console.log(obj);//{ name: 'A', age: 33, sex: 'F' },因为括号中先计算,','号运算符从左往右,所以obj == { name: 'A', age: 33, sex: 'F' }
//如果想要两个对象,则需要将obj编程数组对象,([{xxx},{xxx}])或者[{xxx},{xxx}]

[{}]:对象数组,该数组的元素是对象

var arr = [{name:'Tom'},{name:'Jerry'}];//arr数组有两个元素{name:'Tom'}和{name:'Jerry'}
console.log(arr[0].name);//'Tom'

4.JSON和String转换

JSON和String转换我的优快云博客链接

String转换成JSON (String内为单JSON对象)

var querystring = require('querystring');
var str = "a=b&c=d&e=f&h=g";

var str1 = `
{
    "name": "11",
    "age": 22,
    "sex": "F"
}
`;
//方式1 注意:字符串的属性名必须有引号,且为双引号,否则报错。得到的结果:非特殊属性名的无引号
var json11 = JSON.parse(str1);

//方式2 结果:非特殊属性名的无引号
var json12 = eval('(' + str1 + ')');

//方式3 结果:非特殊属性名的无引号
var json13 = (function (obj) {
    return Function('return (' + obj + ')')();
})(str1);
// 这里'return (' + obj + ')'其实将字符串作为构造函数Function的代码参数传入
// 为什么obj两端要加(),是为了避免将{}作为包裹代码段

//方式4,将含&和=的字符串转换成JSON对象,&替换为,,=替换为:,属性名无引号,属性值全为字符串
var json14 = querystring.parse(str);//需要导入querystring模块

String转换成JSON (String内为多JSON对象)

var str2 = `
    {
        "name": "LiLi",
        "age": 22,
        "sex": "F"
    },
    {
        "name": "LaLa",
        "age": 18,
        "sex": "F"
    }
`;
//方式1 注意:字符串的属性名必须有引号。得到的结果:非特殊属性名的无引号
var json21 = JSON.parse('['+str2+']');

//方式2 结果:非特殊属性名的无引号
var json22 = eval('[' + str2 + ']');

//方式3 结果:非特殊属性名的无引号
var json23 = (function (obj) {
    return Function('return [' + obj + ']')();
})(str2);

JSON转换成String

var querystring = require('querystring');
var str = "a=b&c=d&e=f&h=g";
var json1 = ({ name: '11', age: 22, sex: 'F' });
var json2 = querystring.parse(str);
var json3 = [
  { name: 'LiLi', age: 22, sex: 'F' },
  { name: 'LaLa', age: 18, sex: 'F' }
];
//方式1 所有属性名加"",json的最外层加""
var str11 = JSON.stringify(json1);
var str21 = JSON.stringify(json2);
var str31 = JSON.stringify(json3);

自定义函数

自己封装两者转换的函数,待补充。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值