参照文档:学习JavaScript这一篇就够了
并非涵盖所有知识点,只是记录在学习过程中需要特殊记下的问题,大部分的知识点,看上面的链接
1 JavaScript 基础
JavaScript是严格区分大小写的,也就是abc和Abc会被解析器认为是两个不同的东西。
1.1 JavaScript引用
1.1.1 行内式
<body>
<!-- 行内式 -->
<input type="button" value="点我试试" onclick="alert('Hello World')" />
</body>
1.1.2 内嵌式
<body>
</body>
<script>
// 嵌入式
alert('自动显示');
</script>
1.1.3 外链式
test.html
<body>
</body>
<script src="Test.js"></script>
Test.js
alert('外链式');
1.2 JavaScript的输出
<script>
// 向页面输出一句话
document.write("Hello,World!");
// 向控制台输出
console.log("输出一条日志"); // 最常用
console.info("输出一条信息");
console.warn("输出一条警告");
console.error("输出一条错误");
</script>
运行结果:
1.3 基本数据类型
JavaScript中一共有5种基本数据类型:
- 字符串型(String)
- 数值型(Number)
- Number表示的数字大小是有限的,如果超过了这个范围,则会返回 ±Infinity。
- 最大值:+1.7976931348623157e+308
- 最小值:-1.7976931348623157e+308
- 0以上的最小值:5e-324
- 特殊的数字:
- Infinity:正无穷
- -Infinity:负无穷
- NaN:非法数字(Not A Number)
- 其它的进制:
- 二进制:0b 开头表示二进制,但是,并不是所有的浏览器都支持
- 八进制:0 开头表示八进制
- 十六进制:0x 开头表示十六进制
- Number表示的数字大小是有限的,如果超过了这个范围,则会返回 ±Infinity。
注意:使用typeof检查一个Number类型的数据时(包括NaN 和 Infinity),会返回"number"。
- 布尔型(Boolean)
- undefined型(Undefined)
Undefined 类型只有一个值,即特殊的 undefined。
在使用 var 声明变量但未对其加以初始化时,这个变量的值就是 undefined。 - null型(Null)
Null 类型是第二个只有一个值的数据类型,这个特殊的值是 null。
undefined值实际上是由null值衍生出来的,所以如果比较undefined和null是否相等,会返回true
注意:从语义上看null表示的是一个空的对象,所以使用typeof检查null会返回一个Object。
<script>
console.log('typeof (true)' + '\t\t: ' + typeof (true));
console.log('typeof (NaN)' + '\t\t: ' + typeof (NaN));
console.log('typeof (undefined)' + '\t: ' + typeof (undefined));
console.log('typeof (null)' + '\t\t: ' + typeof (null));
</script>
运行结果:
这5种之外的类型都称为Object,所以总的来看JavaScript中共有六种数据类型。
1.4 逻辑运算符(&&,||)
特点:短路运算
短路运算的原理:当有多个表达式(值)时,左边的表达式值可以确定结果时,就不再继续运算右边的表达式的值
<script>
//短路运算
//短路运算的原理:当有多个表达式(值)时,左边的表达式值可以确定结果时,就不再继续运算右边的表达式的值
//逻辑与
var a = 0;
var b = 11;
console.log(a++ && b++); //0 (a++为非,即中断,打印a++的值,b++不处理)
console.log('a=' + a, 'b=' + b); //a=1 b=11
console.log('----------');
a = 1;
b = 0;
console.log(a++ && b++); //0 (a++为真,b++为非,执行至b++,打印b++的值)
console.log('a=' + a, 'b=' + b); //a=2 b=1
console.log('----------');
//逻辑或
a = 1;
b = 11;
console.log(a++ || b++); //0 (a++为真,即中断,打印a++的值)
console.log('a=' + a, 'b=' + b); //a=2 b=11
console.log('----------');
a = 0;
b = 11;
console.log(a++ || b++); //11 (a++为非,执行至b++,打印b++的值)
console.log('a=' + a, 'b=' + b); //a=1 b=12
</script>
1.5 函数
- 形参的默认值为 undefined,所以当实参数量小于形参时,多余的形参值为undefined。
- 如果函数没有 return ,返回的值是 undefined
<script>
function fun1(n1, n2) {
console.log(n1); // 1
console.log(n2); // undefined
}
var ret1 = fun1(1);
console.log(ret1); // undefined
function fun2() {
return "a", "b";
}
console.log(fun2()); // b
</script>
1.5.1 构造函数
构造函数就是一个普通的函数,创建方式和普通函数没有区别
不同点:
- 构造函数习惯上首字母大写
- 调用方式的不同,普通函数是直接调用,而构造函数需要使用new关键字来调用。
构造函数 new调用 时的处理:
- 它会立刻在内存中创建一个新的空对象。
- 让 this 指向这个新的对象。
- 执行构造函数里面的代码,给这个新对象添加属性和方法
- 返回这个新对象(所以构造函数里面不需要return)
1.6 this对象
解析器在调用函数每次都会向函数内部传递进一个隐含的参数,这个隐含的参数就是this,this指向的是一个对象,这个对象我们称为函数执行的上下文对象,根据函数的调用方式的不同,this会指向不同的对象
- 以函数的形式调用时,this永远都是window
- 以方法的形式调用时,this就是调用方法的那个对象
- 当以构造函数的形式调用时,this就是新创建的那个对象
- 使用call和apply调用时,this是传入的那个指定对象
如果一个函数作为一个对象的属性保存,那么我们称这个函数是这个对象的方法,调用这个函数就说调用对象的方法(method)。
注意:方法和函数只是名称上的区别,没有其它别的区别
<script>
//创建一个全局变量:myName
var myName = "全局myName";
//创建一个函数
function sayName() {
console.log(this);
console.log(this.myName);
}
//创建一个对象
var obj = {
myName: "objName",
sayMyName: sayName
};
//以函数的形式调用时,this永远都是window
// this : Window{......}
// this.myName : 全局myName
sayName();
//以方法的形式调用时,this就是调用方法的那个对象
// this : {myName: 'objName'....}
// this.myName : objName
obj.sayMyName();
// 使用构造函数来创建对象
function Person(name, age) {
// 设置对象的属性
this.name = name;
this.age = age;
// 设置对象的方法
this.sayName = function () {
// 当以构造函数的形式调用时,this就是新创建的那个对象
console.log(this); // this : Person {name: 'testName',......}
console.log(this.name); // this.name : testName
};
}
var person1 = new Person("testName", 18);
person1.sayName();
</script>
1.7 原型(prototype, __proto__)
构造函数方法存在浪费内存的问题:
使用构造函数的方式进行创建对象,它还是存在一个问题,每一个对象的属性不一样这是一定的,但是它的方法似乎好像是一样的,如果我创建1000个对象,那岂不是内存中就有1000个相同的方法,这就会造成浪费内存的问题。
解决方法
:我们可以把函数抽取出来,作为全局函数,在构造函数中直接引用就可以了
// 使用构造函数来创建对象
function Person(name, age) {
// 设置对象的属性
this.name = name;
this.age = age;
// 设置对象的方法
this.sayName = sayName
}
// 把函数抽取出来为全局函数
function sayName() {
console.log(this.name);
}
var person1 = new Person("test", 18);
person1.sayName();
但是,在全局作用域中定义函数不是一个好的办法。
因为,如果要是涉及到多人协作开发一个项目,别人也有可能叫sayName这个方法,这样在工程合并的时候就会导致一系列的问题,污染全局作用域。
有没有一种方法,我只在Person这个类的全局对象中添加一个函数,然后在类中引用?
答案就是使用原型对象
原型(prototype):
我们创建的每一个函数,解析器都会向函数中添加一个属性prototype,这个属性对应着一个对象,这个对象就是我们所谓的原型对象
,即显式原型
原型对象
就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象
,我们可以将对象中共有的内容,统一设置到原型对象
中。
函数以普通函数的形式调用prototype时,没有任何作用
函数以构造函数的形式调用prototype时,它所创建的对象中都会有一个隐含的属性,指向该构造函数的原型对象,我们可以通过__proto__(隐式原型)来访问该属性。
__proto__:
对象都会有一个属性 proto 指向构造函数的prototype原型对象,之所以我们对象可以使用构造函数prototype原型对象的属性和方法,就是因为对象有__proto__原型的存在。
__proto__对象原型和原型对象 prototype 是等价的(参照下图)
__proto__对象原型的意义就在于为对象的查找机制提供一个方向,或者说一条路线,但是它是一个非标准属性,因此实际开发中,不可以使用这个属性,它只是内部指向原型对象 prototype
<script>
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayName = function () {
console.log(this.name);
}
Person.prototype.sayAge = function () {
console.log(this.age);
}
var p1 = new Person("P1", 18);
//构造函数.prototype
console.log(Person.prototype);
//对象.__proto__
console.log(p1.__proto__);
//两者指向相同
if (p1.__proto__ === Person.prototype) {
console.log("true");
}
</script>
运行结果:
constructor 构造函数:
对象原型(__ proto __) 和构造函数(prototype)原型对象,里面都有一个属性 constructor 属性, constructor 我们称为构造函数,因为它指回构造函数本身。
constructor主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数
如果有多个对象的方法,我们可以给原型对象prototype采取对象形式赋值(参照下图),但是这样会覆盖构造函数原型对象原来的内容,这样修改后的原型对象constructor就不再指向当前构造函数了。此时,我们可以在修改后的原型对象中,添加一个constructor指向原来的构造函数
<script>
function Boy(name, age) {
this.name = name;
this.age = age;
}
Boy.prototype.sayName = function () {
console.log(this.name);
}
Boy.prototype.sayAge = function () {
console.log(this.age);
}
console.log(Boy.prototype);
var John = new Boy("John", 18);
function Girl(name, age) {
this.name = name;
this.age = age;
}
Girl.prototype = {
sayName: function () {
console.log(this.name);
},
sayAge: function () {
console.log(this.age);
}
}
console.log(Girl.prototype);
function Girl_Fix(name, age) {
this.name = name;
this.age = age;
}
Girl_Fix.prototype = {
sayName: function () {
console.log(this.name);
},
sayAge: function () {
console.log(this.age);
},
constructor: Girl_Fix
}
console.log(Girl_Fix.prototype);
</script>
运行结果:
构造函数、实例、原型对象三者关系
1.8 原型链
- 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性
- 如果没有就查找它的原型(也就是_proto_指向的prototype原型对象)
- 如果还没有就查找原型对象的原型(Object的原型对象),依次类推一直找到Object为止(null)
- __ proto __对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线
<script>
function Fruit(type, count) {
this.type = type;
this.count = count;
//对象自身有,原型里也有,执行自身中的
this.printType = function () {
console.log("Type-1:" + this.type);
}
}
Fruit.prototype.printType = function () {
console.log("Type-2:" + this.type);
}
//对象自身没有,原型里有,执行原型里的
Fruit.prototype.printCount = function () {
console.log("Count-2:" + this.count);
}
var apple = new Fruit("apple", 10);
apple.printType();
apple.printCount();
</script>
执行结果:
1.9 属性/方法的存在检查
in
方法:检查对象的自身或者原型中,某个属性/方法是否存在。hasOwnProperty()
方法:仅检查对象的自身中,某个属性/方法是否存在。
<script>
// 创造一个构造函数
function Fruit() {
}
// 向Fruit的原型中添加一个name属性
Fruit.prototype.type = "ALL";
// 创建一个Fruit的实例
var apple = new Fruit();
apple.cnt = 10;
// 使用in检查对象中是否含有某个属性
// 对象自身中有,或者原型中有,都会返回true
console.log("cnt" in apple); //true 仅在自身中有
console.log("type" in apple); //true 仅在原型中有
// 使用对象的hasOwnProperty()来检查对象自身中是否含有该属性
// 使用该方法只有当对象自身中含有属性时,才会返回true
console.log(apple.hasOwnProperty("cnt")); //true 仅在自身中有
console.log(apple.hasOwnProperty("type")); //false 仅在原型中有
console.log("-------------------");
// Fruit类对象中没有hasOwnProperty这个方法啊,它是哪来的?
// 就是原型中的,在执行方法的时候它会通过原型链进行查找,这个方法是Object的特有方法
// 检查当前对象
console.log(apple.hasOwnProperty("hasOwnProperty")); //false
// 检查当前对象的原型对象
console.log(apple.__proto__.hasOwnProperty("hasOwnProperty")); //false
// 检查当前对象的原型对象的原型对象
console.log(apple.__proto__.__proto__.hasOwnProperty("hasOwnProperty")); //true
</script>
1.10 数组
1.10.1 创建数组
- 使用对象创建
var arr = new Array(); arr[0] = 1; arr[1] = 2; arr[2] = 3; arr[3] = 4; arr[4] = 5; arr[5] = 6;
- 使用字面量创建
var arr = [1, 2, 3, 4, 5, 6];
1.10.2 遍历数组
for (var i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
1.10.3 数组方法
-
push()
:向数组的末尾添加一个或多个元素,并返回数组的新的长度。 -
pop()
:删除数组的最后一个元素,并将被删除的元素作为返回值返回。 -
unshift()
:向数组开头添加一个或多个元素,并返回新的数组长度。 -
shift()
:删除数组的第一个元素,并将被删除的元素作为返回值返回。 -
forEach()
:用来遍历数组
forEach()方法需要一个函数作为参数,像这种函数,由我们创建但是不由我们调用的,我们称为回调函数。
数组中有几个元素函数就会执行几次,每次执行时,浏览器会将遍历到的元素,以实参的形式传递进来
我们可以来定义形参,来读取这些内容。
浏览器会在回调函数中传递三个参数:- 第一个参数:当前正在遍历的元素
- 第二个参数:当前正在遍历的元素的索引
- 第三个参数:当前正在遍历的数组
注意:这个方法只支持IE8以上的浏览器,IE8及以下的浏览器均不支持该方法,所以如果需要兼容IE8,
则不要使用forEach(),还是使用for循环来遍历数组。var arr = ["L1", "L2", "L3"]; arr.forEach(function (value, index, array) { console.log(value + " ### " + index + " ### " + array); }); //执行结果 //L1 #### 0 #### L1,L2,L3 //L2 #### 1 #### L1,L2,L3 //L3 #### 2 #### L1,L2,L3
-
slice()
:用来从数组提取指定元素,该方法不会改变原数组,而是将截取到的元素封装到一个新数组中并返回
参数:- 第一个参数:截取开始的位置的索引,包含开始索引
- 第二个参数:截取结束的位置的索引,不包含结束索引
第二个参数可以省略不写,此时会截取从开始索引往后的所有元素
注意:索引可以传递一个负值,如果传递一个负值,则从后往前计算,-1代表倒数第一个,-2代表倒数第二个。
var arr = ["L1", "L2", "L3", "L4", "L5"]; //index:1-3的元素取得 var result = arr.slice(1, 4); //['L2', 'L3', 'L4'] console.log(result); //index:3 之后所有元素取得 result = arr.slice(3); //['L4', 'L5'] console.log(result); //index:1 到 倒数第二个之前一个元素 取得 result = arr.slice(1, -2); //['L2', 'L3'] console.log(result);
-
splice()
:用于删除数组中的指定元素,该方法会影响到原数组,会将指定元素从原数组中删除,
并将被删除的元素作为返回值返回
参数:- 第一个参数:表示开始位置的索引
- 第二个参数:表示要删除的元素数量
- 第三个参数及以后参数:可以传递一些新的元素,这些元素将会自动插入到开始位置索引前边
var arr = ["L1", "L2", "L3", "L4", "L5"]; var result = arr.splice(3, 2); console.log(arr); // ['L1', 'L2', 'L3'] console.log(result); // ['L4', 'L5'] result = arr.splice(1, 0, "L6", "L7", "L8"); console.log(arr); // ['L1', 'L6', 'L7', 'L8', 'L2', 'L3'] console.log(result); // []
-
concat()
:可以连接两个或多个数组,并将新的数组返回,该方法不会对原数组产生影响 -
join()
:可以将数组转换为一个字符串,该方法不会对原数组产生影响,而是将转换后的字符串作为结果返回。在join()中可以指定一个字符串作为参数,这个字符串将会成为数组中元素的连接符,如果不指定连接符,则默认使用","作为连接符
var arr = ["L1", "L2", "L3"]; console.log(arr.join()); //L1,L2,L3 console.log(arr.join("#")); //L1#L2#L3
-
reverse()
:用来反转数组(前边的去后边,后边的去前边),该方法会直接修改原数组 -
sort()
:用来对数组中的元素进行排序,会影响原数组,默认会按照Unicode编码进行排序对于纯数字的数组,可能会得到错误的结果,解决方案参照一下代码
var listLetter = ["a", "d", "b", "c", "f", "e"]; listLetter.sort(); console.log(listLetter); //['a', 'b', 'c', 'd', 'e', 'f'] // 对于纯数字的数组,使用sort()排序时,也会按照Unicode编码来排序 // 所以对数字进排序时,可能会得到错误的结果。 var listNumber = [1, 3, 2, 4, 10, 11]; listNumber.sort(); console.log(listNumber); //[1, 10, 11, 2, 3, 4] // 解决方案: // 可以在sort()添加一个回调函数,来指定排序规则,回调函数中需要定义两个形参 // 浏览器会分别使用数组中的元素作为实参去调用回调函数,具体使用哪个元素调用不确定, // 但是肯定的是在数组中a一定在b前边,浏览器会根据回调函数的返回值来决定元素的顺序, // 如下: // 如果返回一个大于0的值,则元素会交换位置 // 如果返回一个小于0的值,则元素位置不变 // 如果返回一个等于0的值,则认为两个元素相等,也不交换位置 // 经过上边的规则,我们可以总结下: // 如果需要升序排列,则返回 a-b // 如果需要降序排列,则返回 b-a listNumber.sort(function (a, b) { return a - b; }); console.log(listNumber); //[1, 2, 3, 4, 10, 11]
-
filter()
:筛选对象数组中符合条件的所有元素,返回一个新数组// filter 筛选数组 var arr = [1, 2, 3, 10, 11, 12]; // function(currentValue, index, array) var newArr = arr.filter(function (value, index) { return value >= 10 || index == 0; }); console.log(newArr); //[1, 10, 11, 12]
-
some()
:用于查找数组中是否有满足条件的元素
返回的是布尔值
true:数组中有满足条件的元素
false:数组中没有如果找到一个满足条件的元素,则终止循环,不再继续查找
var arr = ['L1', 'L2', 'L3']; var flag1 = arr.some(function (value, index, array) { return value == 'L2'; }); console.log(flag1); //true var flag2 = arr.some(function (value, index, array) { return value == 'L4'; }); console.log(flag2); //false
1.11 call()和apply()
这两个方法都是函数对象的方法,需要通过函数对象来调用,当对函数调用call()和apply()都会调用函数执行
在调用call()和apply()将一个对象指定为第一个参数,此时这个对象将会成为函数执行时的this
call():可以将实参在对象之后依次传递
apply():需要将实参封装到一个数组中统一传递
function fun(a, b) {
console.log("a = " + a);
console.log("b = " + b);
console.log("fun = " + this);
}
var obj = {
name: "obj",
sayName: function () {
console.log(this.name);
}
};
fun(1, 2);
//运行结果:
// a = 1
// b = 2
// fun = [object Window]
console.log("===============");
//call()
fun.call(obj, 3, 4);
//运行结果:
// a = 3
// b = 4
// fun = [object Object]
console.log("===============");
//apply()
fun.apply(obj, [5, 6]);
//运行结果:
// a = 5
// b = 6
// fun = [object Object]
1.11 arguments参数
在调用函数时,浏览器每次都会传递进两个隐含的参数:
this
:函数的上下文对象arguments
:封装实参的伪数组对象
arguments是一个类数组对象,它也可以通过索引来操作数据,也可以获取长度
在调用函数时我们所传递的实参都会在arguments中保存,我们即使不定义形参,也可以通过arguments来使用实参,只不过比较麻烦不具有数组的push(), pop()等方法
它里边有一个属性叫做callee,这个属性对应一个函数对象,就是当前正在指向的函数的对象。function fun(a, b) { // 通过下标获取第一个参数 console.log(arguments[0]); // A1 // 通过下标获取第二个参数 console.log(arguments[1]); // A2 // 获取实参的个数 console.log(arguments.length); // 2 // callee console.log(arguments.callee === fun); // true } fun("A1", "A2");
1.12 预解析
JavaScript代码是由浏览器中的JavaScript 解析器来执行的。
JavaScript解析器在运行JavaScript代码的时候分为两步:
-
预解析
:js引擎会把js里面所有的 var 还有 function 提升到当前作用域的最前面- 变量的声明会被提升到当前作用域的最上面,变量的赋值不会提升
- 函数的声明会被提升到当前作用域的最上面,但是不会调用函数。
函数为函数表达式方式声明时,调用必须写在函数声明的下面
<script> /* 变量提升 */ console.log(num); // undefined var num = 10; //相当于执行了以下代码 var num; // 变量声明提升到当前作用域最上面 console.log(num); num = 10; // 变量的赋值不会提升 //===================================================== /* 函数提升 */ fn(); //11 function fn() { console.log('11'); } //相当于执行了以下代码 function fn() { console.log('11'); } fn(); //11 //===================================================== /* 函数表达式 */ // 匿名函数(函数表达式方式):若我们把函数调用放在函数声明上面 fn(); var fn = function () { console.log('22'); // 报错 } //相当于执行了以下代码 var fn; fn(); // fn没赋值,没这个,报错 var fn = function () { console.log('22'); // 报错 } </script>
-
代码执行
:从上到下执行JS语句
学习预解析能够让我们知道为什么在变量声明之前访问变量的值是 undefined,为什么在函数声明之前就可以调用函数。
注意:
var a = b = c = 6;
=>
var a = 6; b = 6; c = 6; //若在函数申明内部,b,c为全局变量
<script>
f1();
console.log(c);
console.log(b);
console.log(a);
function f1() {
// 相当于 var a = 9; b = 9;c = 9; b和c的前面没有var声明,当全局变量看
// 集体声明 var a = 9,b = 9,c = 9;
var a = b = c = 9;
console.log(a);
console.log(b);
console.log(c);
}
//相当于执行了以下操作
function f1() {
var a;
a = b = c = 9;
console.log(a); //9
console.log(b); //9
console.log(c); //9
}
f1();
console.log(c); //9
console.log(b); //9
console.log(a); //报错 a是局部变量
</script>
2. JavaScript DOM
2.1 console.log()和console.dir()
console.log()
:无法看到标签对象的属性和方法,能输出多个数据,数据类型颜色有差异。
console.dir()
:以对象形式输出,只能输出一个数据,不能直观识别数据类型,但是可以看到标签对象的属性和方法。
<body>
<ul id="list">
<li>L1</li>
<li>L2</li>
<li>L3</li>
</ul>
<script>
var lst = document.getElementById('list');
console.log(lst);
console.dir(lst);
</script>
</body>
运行结果:
2.2 style, currentStyle, getComputedStyle()
元素.style.样式
:设置和读取的都是行内样式,无法读取样式表中的样式或者说正在应用的样式,通过style属性设置的样式都是行内样式(优先级较高)
元素.currentStyle.样式
:读取当前正在应用的样式属性,用来读取当前元素正在显示的样式,
如果当前元素没有设置该样式,则获取它的默认值,但是currentStyle只有IE浏览器支持
getComputedStyle()
:在其它浏览器中可以使用这个方法来获取元素当前的样式,这个方法是window的方法,可以直接使用
需要两个参数:
第一个参数:要获取样式的元素
第二个参数:可以传递一个伪元素,一般都传null
该方法会返回一个对象,对象中封装了当前元素对应的样式,可以通过 对象.样式名 来读取样式,如果获取的样式没有设置,则会获取到真实的值,而不是默认值,该方法不支持IE8及以下的浏览器。
通过
currentStyle
和getComputedStyle()
读取到的样式都是只读的,不能修改
如果要修改必须通过style
属性,因此,可以写一个适配各个浏览器的读取元素样式的方法,参照以下代码
CSS的样式名中有些名称含有"-",这种名称在JS中是不合法的比如background-color,需要将这种样式名去掉-,修改为驼峰命名法
<head>
<meta charset="UTF-8">
<title></title>
<style>
/*样式表的样式*/
#box {
width: 200px;
height: 200px;
background-color: pink;
}
</style>
</head>
<body>
<div style="width: 100px;background-color: green;" id="box"></div>
<script>
var box = document.getElementById("box");
console.log(box.style.width); //100px 取得行内样式 width: 100px;
console.log(box.style.height); // 行内样式不存在
console.log(box.style.backgroundColor); //green 转换驼峰名字
box.style.width = "150px"; //变更行内样式 width: 150px;
/*通用的获取元素样式的方法*/
function getStyle(obj, name) {
if (window.getComputedStyle) {
//正常浏览器的方式,具有getComputedStyle()方法
return getComputedStyle(obj, null)[name];
} else {
//IE8的方式,没有getComputedStyle()方法
return obj.currentStyle[name];
}
}
console.log(getStyle(box, "width"));
console.log(getStyle(box, "height"));
console.log(getStyle(box, "background-color"));
</script>
</body>
2.3 节点
2.3.1 节点类型
节点 | 常量名 | 常量值 | 节点类型 |
---|---|---|---|
元素节点 | Node.ELEMENT_NODE | 1 | Element |
属性节点 | Node.ATTRIBUTE_NODE | 2 | Attr |
文本节点 | Node.TEXT_NODE | 3 | Text |
CDATA节点 | Node.CDATA_SECTION_NODE | 4 | CDATASection |
实体引用名称节点 | Node.ENTRY_REFERENCE_NODE | 5 | EntityReference |
实体名称节点 | Node.ENTITY_NODE | 6 | Entity |
处理指令节点 | Node.PROCESSING_INSTRUCTION_NODE | 7 | ProcessingInstruction |
注释节点 | Node.COMMENT_NODE | 8 | Comment |
文档节点 | Node.DOCUMENT_NODE | 9 | Document |
文档类型节点 | Node.DOCUMENT_TYPE_NODE | 10 | DocumentType |
文档片段节点 | Node.DOCUMENT_FRAGMENT_NODE | 11 | DocumentFragment |
DTD声明节点 | Node.NOTATION_NODE | 12 | Notation |
※红色
为常用的节点类型
<body>
<ul id="ul1">
<li id="li1">L1</li>
</ul>
<script>
var ul1 = document.getElementById("ul1");
console.log(ul1); // <ul id="ul1">...</ul>
console.log(ul1.nodeType); // 1
console.log(ul1.nodeName); // UL
console.log(ul1.nodeValue); // null
console.log("=================");
var ul1_attr = ul1.getAttributeNode('id');
console.log(ul1_attr); // id="ul1"
console.log(ul1_attr.nodeType); // 2
console.log(ul1_attr.nodeName); // id
console.log(ul1_attr.nodeValue); // ul1
console.log("=================");
var ul1_fc = ul1.firstChild;
console.log(ul1_fc); // #text
console.log(ul1_fc.nodeType); // 3
console.log(ul1_fc.nodeName); // #text
console.log(ul1_fc.nodeValue); //
console.log("=================");
console.log(document); // #document
console.log(document.nodeType); // 9
console.log(document.nodeName); // #document
console.log(document.nodeValue); // null
console.log("=================");
</script>
</body>
2.3.2 节点使用
方法 | 描述 |
---|---|
元素节点.parentNode | 返回元素的父节点。 |
元素节点.parentElement | 返回元素的父元素。 |
元素节点.childNodes | 返回元素的一个子节点的数组(包含空白文本Text节点)。 |
元素节点.children | 返回元素的一个子元素的集合(不包含空白文本Text节点)。 |
元素节点.firstChild | 返回元素的第一个子节点(包含空白文本Text节点)。 |
元素节点.firstElementChild | 返回元素的第一个子元素(不包含空白文本Text节点)。(IE9以上支持) |
元素节点.lastChild | 返回元素的最后一个子节点(包含空白文本Text节点)。 |
元素节点.lastElementChild | 返回元素的最后一个子元素(不包含空白文本Text节点)。(IE9以上支持) |
元素节点.previousSibling | 返回某个元素紧接之前节点(包含空白文本Text节点)。 |
元素节点.previousElementSibling | 返回指定元素的前一个兄弟元素(相同节点树层中的前一个元素节点)。(IE9以上支持) |
元素节点.nextSibling | 返回某个元素紧接之后节点(包含空白文本Text节点)。 |
元素节点.nextElementSibling | 返回指定元素的后一个兄弟元素(相同节点树层中的下一个元素节点)。(IE9以上支持) |
document.createElement(‘XXX’) | 创建’XXX’指定的HTML元素 |
元素节点.appendChild() | 将一个节点添加到指定父节点的子节点列表末尾。 |
元素节点.insertBefore() | 将一个节点添加到指定父节点的指定子节点前面。 |
元素节点.removeChild() | 将一个节点从指定父节点中删除,并返回删除的节点。 |
元素节点.cloneNode() | 返回调用该方法的节点的一个副本。 也称为克隆节点/拷贝节点 括号参数:空/ false ,则是浅拷贝,即只克隆复制节点本身,不克隆里面的子节点 括号参数:true ,则是深度拷贝,会复制节点本身以及里面所有的子节点 |
2.4 三种动态创建元素的区别
- document.write() 是直接将内容写入页面的内容流,但是文档流执行完毕,它会导致页面全部重绘
- innerHTML 是将内容写入某个 DOM 节点,不会导致页面全部重绘,创建多个元素效率更高(不要拼接字符串,采取数组形式拼接),结构稍微复杂
- createElement()创建多个元素效率稍低一点点,但是结构更清晰
总结:不同浏览器下, innerHTML 效率要比 createElement 高
2.5 DOM文档事件
2.5.1 解决事件对象的兼容性问题
当事件的响应函数被触发时,浏览器每次都会将一个事件对象作为实参传递进响应函数。
Event对象代表事件的状态,比如事件在其中发生的元素、键盘按键的状态、鼠标的位置、鼠标的状态。
在IE8及以下的浏览器中,响应函数被触发时,浏览器不会传递事件对象,是将事件对象作为window对象的属性保存的。
解决方案: event = event || window.event;
document.onkeydown = function (event) {
event = event || window.event;
//.....
};
3 todo
JSON
AJAX
Cookie
WebStorage
闭包
版权声明:文章中部分文档参照了CSDN博主「轻松的小希」的原创文章
原文链接:https://blog.csdn.net/qq_38490457/article/details/109257751
版权声明:文章中部分文档参照了CSDN博主「生命是有光的」的原创文章
原文链接:https://blog.csdn.net/Augenstern_QXL/article/details/115219073