每日学习小结

10/2

  1. css默认文字行高12px;

  2. em是当前html根节点的倍数,一般所有浏览器的html根元素font-size:16px,1rem = 16px;em是当前标签父标签的倍数

  3. 相邻兄弟选择器(Adjacent sibling selector)可选择紧接在另一元素后的元素,且二者有相同父元素;而‘~’选择器则表示某元素后所有同级的指定元素,强调所有的。

  4. 如何用css写出一个三角形
    :{
    width: 0;
    height: 0;
    border: 100px solid;
    border-color: pink transparent transparent transparent;
    }

  5. [title]{color:red;} CSS 属性选择器:对带有指定属性的 HTML 元素设置样式。(常见的:= ~=)

  6. JQuery使用css选择器写法优化代码$(“#id”)等于document.getElementById(“id”)

  7. es6中新添加的map set has

10/3

  1. es6中在原来5中基本数据类型的基础上加了个Symbol,它的功能类似于一种标识唯一性的ID。我们需要重点记住的一点是:每个Symbol实例都是唯一的。Symbol类型的key是不能通过Object.keys()或者for…in来枚举的,它未被包含在对象自身的属性名集合(property names)之中。所以,利用该特性,我们可以把一些不需要对外操作和访问的属性使用Symbol来定义。我们可以利用这一特点来更好的设计我们的数据对象,让“对内操作”和“对外选择性输出”变得更加优雅。
    具体参考:https://www.jianshu.com/p/f40a77bbd74e

  2. 简单学习了一些前后端是如何交互的? 链接:https://www.cnblogs.com/huchong-bk/p/11394090.html

10/4

10/5

10/6

  1. css权重https://www.cnblogs.com/cnblogs-jcy/p/8574177.html

10/12

  1. es6新增语法之${} 模板字符串
    这是es6中新增的字符串方法
    可以配合反单引号完成拼接字符串的功能

10/23

1. 重载

js中没有真正意义上的重载,实际是靠arguments实现
所谓重载,就是一组相同的函数名,有不同个数的参数,在使用时调用一个函数名,传入不同参数,根据你的参数个数,来决定使用不同的函数!但是我们知道js中是没有重载的,因为后定义的函数会覆盖前面的同名函数。

2. LET

ES6 新增了let命令,用来声明局部变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效,而且有暂时性死区的约束。
用let声明的变量,不存在变量提升。而且要求必须 等let声明语句执行完之后,变量才能使用,不然会报Uncaught ReferenceError错误。
https://www.cnblogs.com/fly_dragon/p/8669057.html
关于let变量提升的一些思考!
https://www.jianshu.com/p/b87d620185f2

3. js中变量提升

所有的声明都会提升到作用域的最顶上去。
同一个变量只会声明一次,其他的会被忽略掉。
函数声明的优先级高于变量申明的优先级,并且函数声明和函数定义的部分一起被提升。

4. setTimeout的机制(归档于下面的5.)

5. js事件循环机制

关键词:“非阻塞” 执行栈 事件栈 宏任务 微任务
https://www.cnblogs.com/cangqinglang/p/8967268.html

6. 堆和栈

(堆里存放着一些对象。而栈中则存放着一些基础类型变量以及对象的指针),浅拷贝和深拷贝(深拷贝需要遍历赋值)
https://www.cnblogs.com/allen-xing0910/p/10954231.html

7. web worker

了解到web worker技术,这项技术号称让javascript成为一门多线程语言。
然而,使用web worker技术开的多线程有着诸多限制,例如:所有新线程都受主线程的完全控制,不能独立执行。这意味着这些“线程” 实际上应属于主线程的子线程。另外,这些子线程并没有执行I/O操作的权限,只能为主线程分担一些诸如计算等任务。所以严格来讲这些线程并没有完整的功能,也因此这项技术并非改变了javascript语言的单线程本质。

8. js模块化编程 amd cmd

https://www.cnblogs.com/moxiaowohuwei/p/8692359.html

9. js历史

(1)借鉴C语言的基本语法;
(2)借鉴Java语言的数据类型和内存管理;
(3)借鉴Scheme语言,将函数提升到"第一等公民"(first class)的地位;
(4)借鉴Self语言,使用基于原型(prototype)的继承机制。

10. js中的多态

把强耦合变成松耦合
在JavaScript中大多是通过子类重写父类方法的方式实现多态

//多态写法
var makeSound = function(animal) {
    animal.sound();
}

var Duck = function(){}
Duck.prototype.sound = function() {
    console.log('嘎嘎嘎')
}
var Chiken = function() {};
Chiken.prototype.sound = function() {
    console.log('咯咯咯')
}

makeSound(new Chicken());
makeSound(new Duck());

//非多态写法
var makeSound = function(animal) {
    if(animal instanceof Duck) {
        console.log('嘎嘎嘎');
    } else if (animal instanceof Chicken) {
        console.log('咯咯咯');
    }
}
var Duck = function(){}
var Chiken = function() {};
makeSound(new Chicken());
makeSound(new Duck());

11. es6中的class

//函数名和实例化构造名相同且大写(非强制,但这么写有助于区分构造函数和普通函数)
function Person(name,age) {
    this.name = name;
    this.age=age;
}
Person.prototype.say = function(){
    return "我的名字叫" + this.name+"今年"+this.age+"岁了";
}
var obj=new Person("laotie",88);//通过构造函数创建对象,必须使用new 运算符
console.log(obj.say());//我的名字叫laotie今年88岁了


class Person{//定义了一个名字为Person的类
    constructor(name,age){//constructor是一个构造方法,用来接收参数
        this.name = name;//this代表的是实例对象
        this.age=age;
    }
    say(){//这是一个类的方法,注意千万不要加上function
        return "我的名字叫" + this.name+"今年"+this.age+"岁了";
    }
}
var obj=new Person("laotie",88);
console.log(obj.say());//我的名字叫laotie今年88岁了

class继承
用class定义对象的另一个巨大的好处是继承更方便了。想一想我们从Student派生一个PrimaryStudent需要编写的代码量。现在,原型继承的中间对象,原型对象的构造函数等等都不需要考虑了,直接通过extends来实现:

class PrimaryStudent extends Student {
    constructor(name, grade) {
        super(name); // 记得用super调用父类的构造方法!
        this.grade = grade;
    }

    myGrade() {
        alert('I am at grade ' + this.grade);
    }
}

注意PrimaryStudent的定义也是class关键字实现的,而extends则表示原型链对象来自Student。子类的构造函数可能会与父类不太相同,例如,PrimaryStudent需要name和grade两个参数,并且需要通过super(name)来调用父类的构造函数,否则父类的name属性无法正常初始化。

补充:Object.assign方法来为对象动态增加方法
https://www.jianshu.com/p/86267fab4878

10/24

1. 严格模式

“use strict”;

禁止给未声明的变量赋值;

静默失败升级为错误(静默失败:执行不成功,也不报错!)

禁用了arguments.callee(argument.calle在调用函数时,自动获得当前正在执行的函数本身)
js尽量避免递归,效率太低。
原因:重复量太大

普通函数调用和匿名函数中自调中的this不再指向window,而是undefined

2. OOP增强学习之静态方法,实例方法

静态方法:不需要任何实例,就可用类型名直接调用的方法(如 Object.setPrototypeOf() parseFloat())
实例方法:必须在实例的基础上进行方法的调用

3. 保护属性

将所有属性分为2大类:
命名属性:可用,访问到的属性
      又分为2大类:
        数据属性:直接存储属性值的属性
        访问器属性:不实际存储属性值,仅负责提供对另一个数据属性的保护。
内部属性:不能用,访问到的属性

保护结构:禁止添加/删除对象中的属性
3个级别:
      (1)防扩展
      Object.preventExtensions(obj)
      禁止给obj对象添加任何新属性
      原理:每个对象都有一个内部属性:extensible:true
      prevenExtensions()将对象的extensible:false
      (2)密封:在兼具防扩展的同时,再禁止删除现有属性Object.seal(obj)
      禁止向obj中添加属性
      禁止删除obj中的属性
      (3)冻结:在兼具密封的基础上,同时禁止修改所有属性值
      Object.freeze(obj);
      禁止向obj中添加属性
      禁止删除obj中的属性
      禁止修改obj中的属性

4. Object.create(); //es6对象新增方法

什么是:创建一个新对象,让新对象继承指定的父对象
何时:在没有构造函数的情况下,也想创建子对象继承父对象时
如何

10/25

1.唯一能判断NaN的方法是通过isNaN()函数:

isNaN(NaN); // true

2.Object.is() 判断两个值是否相同。

如果下列任何一项成立,则两个值相同:
两个值都是 undefined
两个值都是 null
两个值都是 true 或者都是 false
两个值是由相同个数的字符按照相同的顺序组成的字符串
两个值指向同一个对象
两个值都是数字并且
都是正零 +0
都是负零 -0
都是 NaN
都是除零和 NaN 外的其它同一个数字
这种相等性判断逻辑和传统的==运算不同,==运算符会对它两边的操作数做隐式类型转换(如果它们类型不同),然后才进行相等性比较,(所以才会有类似 "" == false 等于 true 的现象),但 Object.is 不会做这种类型转换。

这与 === 运算符的判定方式也不一样。=== 运算符(和== 运算符)将数字值 -0 和 +0 视为相等,并认为 Number.NaN 不等于 NaN。

3.字符串

  1. 多行字符串和模板字符串
    由于多行字符串用\n写起来比较费事,所以最新的ES6标准新增了一种多行字符串的表示方法,用反引号* ... *表示:
`这是一个
多行
字符串`;

var a = "123";
console.log(`这是一个模板字符串,输出${a}`);
  1. 要特别注意的是,字符串是不可变的,如果对字符串的某个索引赋值,不会有任何错误,但是,也没有任何效果:
    var s = ‘Test’;
    s[0] = ‘X’;
    alert(s); // s仍然为’Test’

toUpperCase
toUpperCase()把一个字符串全部变为大写:
var s = ‘Hello’;
s.toUpperCase(); // 返回’HELLO’

toLowerCase
toLowerCase()把一个字符串全部变为小写:
var s = ‘Hello’;
var lower = s.toLowerCase(); // 返回’hello’并赋值给变量lower
lower; // ‘hello’

indexOf ES5新增(联想到match(返回匹配的字符串) search(返回匹配字符串的位置,位置可以多个) exec(返回位置和值))
indexOf()会搜索指定字符串出现的位置:
var s = ‘hello, world’;
s.indexOf(‘world’); // 返回7
s.indexOf(‘World’); // 没有找到指定的子串,返回-1

方法用法作用
indexOf字符串.(检索的字符串,开始检索的位置)返回检索字符串首次出现的位置
match字符串.(检索的字符串或者正则)返回检索的值
search字符串.(检索的字符串或者正则)返回检索值的位置
exec正则.exec(字符串)可通过多次调用exec方法返回所有值和值的位置(index)

substring(联想到substr slice)
substring()返回指定索引区间的子串:
var s = ‘hello, world’
s.substring(0, 5); // 从索引0开始到5(不包括5),返回’hello’
s.substring(7); // 从索引7开始到结束,返回’world’

方法名第一个参数大于第二个参数的情况下参数为负数时第二个参数
substring都为正数,小的参数为起点;第二个参数为负数,返回空字符串;第一个参数为负数,置零;第一个参数为负,置零;第二个参数为负,空字符串;位置
slice不输出结果,静默与字符串长度相加,再判断是否第一个参数大于第二个;位置
substr无影响第一个参数如果为负数,加上字符串长度;第二个参数如果为负数,置零长度

replace
替换字符串中的字符
接受2个参数,第一个参数可以是正则也可以是字符串,第二个参数可以是字符串也可以是函数(函数有3个形参,第一个是匹配的字符串,第二个是匹配字符串在函数中的位置,第三个是原始字符串)

concat ES5新增
拼接字符串,大多数时间还是使用+更为便捷
优雅的深拷贝(slice substring也行)

arr = [1, 2, 3]
arr2 = arr.concat([])
console.log(arr2) //[1, 2, 3]

trim ES5新增
删除前置及后缀的所有空格,返回结果。原字符串不变。

split(联想到join,把数组拼成字符串)
把字符串分割为数组
第一个参数可以是正则也可以是字符串,第二个参数是用来指定数组的大小

4.数组

ES5和ES6数组方法总结:
https://www.cnblogs.com/z-dl/p/8242960.html

注意,直接给Array的length赋一个新的值会导致Array大小的变化:

var arr = [1, 2, 3];
arr.length; // 3
arr.length = 6;
arr; // arr变为[1, 2, 3, undefined, undefined, undefined]
arr.length = 2;
arr; // arr变为[1, 2]

请注意,如果通过索引赋值时,索引超过了范围,同样会引起Array大小的变化:

var arr = [1, 2, 3];
arr[5] = 'x';
arr; // arr变为[1, 2, 3, undefined, undefined, 'x']

splice
splice()方法是修改Array的“万能方法”,它可以从指定的索引开始删除若干元素,然后再从该位置添加若干元素:

var arr = ['Microsoft', 'Apple', 'Yahoo', 'AOL', 'Excite', 'Oracle'];
// 从索引2开始删除3个元素,然后再添加两个元素:
arr.splice(2, 3, 'Google', 'Facebook'); // 返回删除的元素 ['Yahoo', 'AOL', 'Excite']
arr; // ['Microsoft', 'Apple', 'Google', 'Facebook', 'Oracle']
// 只删除,不添加:
arr.splice(2, 2); // ['Google', 'Facebook']
arr; // ['Microsoft', 'Apple', 'Oracle']
// 只添加,不删除:
arr.splice(2, 0, 'Google', 'Facebook'); // 返回[],因为没有删除任何元素
arr; // ['Microsoft', 'Apple', 'Google', 'Facebook', 'Oracle']

map
举例说明,比如我们有一个函数f(x)=x2,要把这个函数作用在一个数组[1, 2, 3, 4, 5, 6, 7, 8, 9]上,就可以用map实现如下:
由于map()方法定义在JavaScript的Array中,我们调用Array的map()方法,传入我们自己的函数,就得到了一个新的Array作为结果:

'use strict';
function pow(x) {
    return x * x;
}
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
var results = arr.map(pow); // [1, 4, 9, 16, 25, 36, 49, 64, 81]
console.log(results);

reduce
再看reduce的用法。Array的reduce()把一个函数作用在这个Array的[x1, x2, x3…]上,这个函数必须接收两个参数,reduce()把结果继续和序列的下一个元素做累积计算,其效果就是:

[x1, x2, x3, x4].reduce(f) = f(f(f(x1, x2), x3), x4)

比方说对一个Array求和,就可以用reduce实现:

var arr = [1, 3, 5, 7, 9];
arr.reduce(function (x, y) {
    return x + y;
}); // 25

filter
也是一个常用的操作,它用于把Array的某些元素过滤掉,然后返回剩下的元素。
和map()类似,Array的filter()也接收一个函数。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是true还是false决定保留还是丢弃该元素。
例如,在一个Array中,删掉偶数,只保留奇数,可以这么写:

var arr = [1, 2, 4, 5, 6, 9, 10, 15];
var r = arr.filter(function (x) {
    return x % 2 !== 0;
});
r; // [1, 5, 9, 15]

把一个Array中的空字符串删掉,可以这么写:

var arr = ['A', '', 'B', null, undefined, 'C', '  '];
var r = arr.filter(function (s) {
    return s && s.trim(); // 注意:IE9以下的版本没有trim()方法
});
r; // ['A', 'B', 'C']

可见用filter()这个高阶函数,关键在于正确实现一个“筛选”函数。

every
every()方法可以判断数组的所有元素是否满足测试条件。
例如,给定一个包含若干字符串的数组,判断所有字符串是否满足指定的测试条件:

var arr = ['Apple', 'pear', 'orange'];
console.log(arr.every(function (s) {
    return s.length > 0;
})); // true, 因为每个元素都满足s.length>0

console.log(arr.every(function (s) {
    return s.toLowerCase() === s;
})); // false, 因为不是每个元素都全部是小写

find
find()方法用于查找符合条件的第一个元素,如果找到了,返回这个元素,否则,返回undefined:

var arr = ['Apple', 'pear', 'orange'];
console.log(arr.find(function (s) {
    return s.toLowerCase() === s;
})); // 'pear', 因为pear全部是小写

console.log(arr.find(function (s) {
    return s.toUpperCase() === s;
})); // undefined, 因为没有全部是大写的元素

findIndex
findIndex()和find()类似,也是查找符合条件的第一个元素,不同之处在于findIndex()会返回这个元素的索引,如果没有找到,返回-1:

var arr = ['Apple', 'pear', 'orange'];
console.log(arr.findIndex(function (s) {
    return s.toLowerCase() === s;
})); // 1, 因为'pear'的索引是1

console.log(arr.findIndex(function (s) {
    return s.toUpperCase() === s;
})); // -1

5.对象

JavaScript用一个{…}表示一个对象,键值对以xxx: xxx形式申明,用,隔开。注意,最后一个键值对不需要在末尾加,,如果加了,有的浏览器(如低版本的IE)将报错。
实际上JavaScript对象的所有属性都是字符串。
不过要小心,如果in判断一个属性存在,这个属性不一定是xiaoming的,它可能是xiaoming继承得到的:

‘toString’ in xiaoming; // true
因为toString定义在object对象中,而所有对象最终都会在原型链上指向object,所以xiaoming也拥有toString属性。

要判断一个属性是否是xiaoming自身拥有的,而不是继承得到的,可以用hasOwnProperty()方法:

var xiaoming = {
    name: '小明'
};
xiaoming.hasOwnProperty('name'); // true
xiaoming.hasOwnProperty('toString'); // false

6.Map

Map是一组键值对的结构,具有极快的查找速度。

var m = new Map([['Michael', 95], ['Bob', 75], ['Tracy', 85]]);
m.get('Michael'); // 95

初始化Map需要一个二维数组,或者直接初始化一个空Map。Map具有以下方法:

var m = new Map(); // 空Map
m.set('Adam', 67); // 添加新的key-value
m.set('Bob', 59);
m.has('Adam'); // 是否存在key 'Adam': true
m.get('Adam'); // 67
m.delete('Adam'); // 删除key 'Adam'
m.get('Adam'); // undefined

由于一个key只能对应一个value,所以,多次对一个key放入value,后面的值会把前面的值冲掉:

var m = new Map();
m.set('Adam', 67);
m.set('Adam', 88);
m.get('Adam'); // 88

7.Set

Set和Map类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在Set中,没有重复的key。

要创建一个Set,需要提供一个Array作为输入,或者直接创建一个空Set:

var s1 = new Set(); // 空Set
var s2 = new Set([1, 2, 3]); // 含1, 2, 3

重复元素在Set中自动被过滤:

var s = new Set([1, 2, 3, 3, '3']);
s; // Set {1, 2, 3, "3"}

注意数字3和字符串’3’是不同的元素。

通过add(key)方法可以添加元素到Set中,可以重复添加,但不会有效果:

s.add(4);
s; // Set {1, 2, 3, 4}
s.add(4);
s; // 仍然是 Set {1, 2, 3, 4}

通过delete(key)方法可以删除元素:

var s = new Set([1, 2, 3]);
s; // Set {1, 2, 3}
s.delete(3);
s; // Set {1, 2}

8.iterable

遍历Array可以采用下标循环,遍历Map和Set就无法使用下标。为了统一集合类型,ES6标准引入了新的iterable类型,Array、Map和Set都属于iterable类型。
具有iterable类型的集合可以通过新的for … of循环来遍历。
for … of循环是ES6引入的新的语法,请测试你的浏览器是否支持:

'use strict';
var a = [1, 2, 3];
for (var x of a) {
}
console.log('你的浏览器支持for ... of');

用for … of循环遍历集合,用法如下:

var a = ['A', 'B', 'C'];
var s = new Set(['A', 'B', 'C']);
var m = new Map([[1, 'x'], [2, 'y'], [3, 'z']]);
for (var x of a) { // 遍历Array
    console.log(x);
}
for (var x of s) { // 遍历Set
    console.log(x);
}
for (var x of m) { // 遍历Map
    console.log(x[0] + '=' + x[1]);
}

你可能会有疑问,for … of循环和for … in循环有何区别?

for … in循环由于历史遗留问题,它遍历的实际上是对象的属性名称。一个Array数组实际上也是一个对象,它的每个元素的索引被视为一个属性。

当我们手动给Array对象添加了额外的属性后,for … in循环将带来意想不到的意外效果:

var a = ['A', 'B', 'C'];
a.name = 'Hello';
for (var x in a) {
    console.log(x); // '0', '1', '2', 'name'
}

for … in循环将把name包括在内,但Array的length属性却不包括在内。

for … of循环则完全修复了这些问题,它只循环集合本身的元素:

var a = ['A', 'B', 'C'];
a.name = 'Hello';
for (var x of a) {
    console.log(x); // 'A', 'B', 'C'
}

这就是为什么要引入新的for … of循环。

然而,更好的方式是直接使用iterable内置的forEach方法,它接收一个函数,每次迭代就自动回调该函数。以Array为例:

'use strict';
var a = ['A', 'B', 'C'];
a.forEach(function (element, index, array) {
    // element: 指向当前元素的值
    // index: 指向当前索引
    // array: 指向Array对象本身
    console.log(element + ', index = ' + index);
});
//A, index = 0
//B, index = 1
//C, index = 2

10/26

1.小心return语句

function foo() {
    return { name: 'foo' };
}

foo(); // { name: 'foo' }

如果把return语句拆成两行:

function foo() {
    return
        { name: 'foo' };
}

foo(); // undefined

要小心了,由于JavaScript引擎在行末自动添加分号的机制,上面的代码实际上变成了:

function foo() {
    return; // 自动添加了分号,相当于return undefined;
        { name: 'foo' }; // 这行语句已经没法执行到了
}

2.generator

generator(生成器)是ES6标准引入的新的数据类型。一个generator看上去像一个函数,但可以返回多次。
ES6定义generator标准的哥们借鉴了Python的generator的概念和语法.

10/27

1.Number

更细心的同学指出,number对象调用toString()报SyntaxError:
123.toString(); // SyntaxError
遇到这种情况,要特殊处理一下:

123…toString(); // ‘123’, 注意是两个点!
(123).toString(); // ‘123’
不要问为什么,这就是JavaScript代码的乐趣!

2.RegExp

分组
除了简单地判断是否匹配之外,正则表达式还有提取子串的强大功能。用()表示的就是要提取的分组(Group)。比如:
^(\d{3})-(\d{3,8})$分别定义了两个组,可以直接从匹配的字符串中提取出区号和本地号码:

var re = /^(\d{3})-(\d{3,8})$/;
re.exec('010-12345'); // ['010-12345', '010', '12345']
re.exec('010 12345'); // null

如果正则表达式中定义了组,就可以在RegExp对象上用exec()方法提取出子串来。
exec()方法在匹配成功后,会返回一个Array,第一个元素是正则表达式匹配到的整个字符串,后面的字符串表示匹配成功的子串。
exec()方法在匹配失败时返回null。
贪婪匹配
需要特别指出的是,正则匹配默认是贪婪匹配,也就是匹配尽可能多的字符。举例如下,匹配出数字后面的0:

var re = /^(\d+)(0*)$/;
re.exec('102300'); // ['102300', '102300', '']

由于\d+采用贪婪匹配,直接把后面的0全部匹配了,结果0*只能匹配空字符串了。

必须让\d+采用非贪婪匹配(也就是尽可能少匹配),才能把后面的0匹配出来,加个?就可以让\d+采用非贪婪匹配:

var re = /^(\d+?)(0*)$/;
re.exec('102300'); // ['102300', '1023', '00']

3.DOM

一种方法是使用querySelector()和querySelectorAll(),需要了解selector语法,然后使用条件来获取节点,更加方便:

// 通过querySelector获取ID为q1的节点:
var q1 = document.querySelector('#q1');

// 通过querySelectorAll获取q1节点内的符合条件的所有节点:
var ps = q1.querySelectorAll('div.highlighted > p');

更新
可以直接修改节点的文本,方法有两种:

一种是修改innerHTML属性,这个方式非常强大,不但可以修改一个DOM节点的文本内容,还可以直接通过HTML片段修改DOM节点内部的子树:

// 获取<p id="p-id">...</p>
var p = document.getElementById('p-id');
// 设置文本为abc:
p.innerHTML = 'ABC'; // <p id="p-id">ABC</p>
// 设置HTML:
p.innerHTML = 'ABC <span style="color:red">RED</span> XYZ';
// <p>...</p>的内部结构已修改

用innerHTML时要注意,是否需要写入HTML。如果写入的字符串是通过网络拿到了,要注意对字符编码来避免XSS攻击。

第二种是修改innerText或textContent属性,这样可以自动对字符串进行HTML编码,保证无法设置任何HTML标签:

// 获取<p id="p-id">...</p>
var p = document.getElementById('p-id');
// 设置文本:
p.innerText = '<script>alert("Hi")</script>';
// HTML被自动编码,无法设置一个<script>节点:
// <p id="p-id">&lt;script&gt;alert("Hi")&lt;/script&gt;</p>

两者的区别在于读取属性时,innerText不返回隐藏元素的文本,而textContent返回所有文本。另外注意IE<9不支持textContent。

修改CSS也是经常需要的操作。DOM节点的style属性对应所有的CSS,可以直接获取或设置。因为CSS允许font-size这样的名称,但它并非JavaScript有效的属性名,所以需要在JavaScript中改写为驼峰式命名fontSize:

// 获取<p id="p-id">...</p>
var p = document.getElementById('p-id');
// 设置CSS:
p.style.color = '#ff0000';
p.style.fontSize = '20px';
p.style.paddingTop = '2em';

当我们获得了某个DOM节点,想在这个DOM节点内插入新的DOM,应该如何做?

如果这个DOM节点是空的,例如,<div></div>,那么,直接使用innerHTML = '<span>child</span>'就可以修改DOM节点的内容,相当于“插入”了新的DOM节点。

如果这个DOM节点不是空的,那就不能这么做,因为innerHTML会直接替换掉原来的所有子节点。

有两个办法可以插入新的节点。一个是使用appendChild,把一个子节点添加到父节点的最后一个子节点。例如:

<!-- HTML结构 -->
<p id="js">JavaScript</p>
<div id="list">
    <p id="java">Java</p>
    <p id="python">Python</p>
    <p id="scheme">Scheme</p>
</div>

<p id="js">JavaScript</p>添加到<div id="list">的最后一项:

var
    js = document.getElementById('js'),
    list = document.getElementById('list');
list.appendChild(js);

现在,HTML结构变成了这样:

<!-- HTML结构 -->
<div id="list">
    <p id="java">Java</p>
    <p id="python">Python</p>
    <p id="scheme">Scheme</p>
    <p id="js">JavaScript</p>
</div>

因为我们插入的js节点已经存在于当前的文档树,因此这个节点首先会从原先的位置删除,再插入到新的位置。

更多的时候我们会从零创建一个新的节点,然后插入到指定位置:

var
    list = document.getElementById('list'),
    haskell = document.createElement('p');
haskell.id = 'haskell';
haskell.innerText = 'Haskell';
list.appendChild(haskell);

这样我们就动态添加了一个新的节点:

<!-- HTML结构 -->
<div id="list">
    <p id="java">Java</p>
    <p id="python">Python</p>
    <p id="scheme">Scheme</p>
    <p id="haskell">Haskell</p>
</div>

动态创建一个节点然后添加到DOM树中,可以实现很多功能。举个例子,下面的代码动态创建了一个<style>节点,然后把它添加到<head>节点的末尾,这样就动态地给文档添加了新的CSS定义:

var d = document.createElement('style');
d.setAttribute('type', 'text/css');
d.innerHTML = 'p { color: red }';
document.getElementsByTagName('head')[0].appendChild(d);

可以在Chrome的控制台执行上述代码,观察页面样式的变化。

insertBefore
如果我们要把子节点插入到指定的位置怎么办?可以使用parentElement.insertBefore(newElement, referenceElement);,子节点会插入到referenceElement之前。

还是以上面的HTML为例,假定我们要把Haskell插入到Python之前:

<!-- HTML结构 -->
<div id="list">
    <p id="java">Java</p>
    <p id="python">Python</p>
    <p id="scheme">Scheme</p>
</div>

可以这么写:

var
    list = document.getElementById('list'),
    ref = document.getElementById('python'),
    haskell = document.createElement('p');
haskell.id = 'haskell';
haskell.innerText = 'Haskell';
list.insertBefore(haskell, ref);

新的HTML结构如下:

<!-- HTML结构 -->
<div id="list">
    <p id="java">Java</p>
    <p id="haskell">Haskell</p>
    <p id="python">Python</p>
    <p id="scheme">Scheme</p>
</div>

可见,使用insertBefore重点是要拿到一个“参考子节点”的引用。很多时候,需要循环一个父节点的所有子节点,可以通过迭代children属性实现:

var
    i, c,
    list = document.getElementById('list');
for (i = 0; i < list.children.length; i++) {
    c = list.children[i]; // 拿到第i个子节点
}

删除DOM
删除一个DOM节点就比插入要容易得多。
要删除一个节点,首先要获得该节点本身以及它的父节点,然后,调用父节点的removeChild把自己删掉:

// 拿到待删除节点:
var self = document.getElementById('to-be-removed');
// 拿到父节点:
var parent = self.parentElement;
// 删除:
var removed = parent.removeChild(self);
removed === self; // true

注意到删除后的节点虽然不在文档树中了,但其实它还在内存中,可以随时再次被添加到别的位置。
当你遍历一个父节点的子节点并进行删除操作时,要注意,children属性是一个只读属性,并且它在子节点变化时会实时更新。
例如,对于如下HTML结构:

<div id="parent">
    <p>First</p>
    <p>Second</p>
</div>

当我们用如下代码删除子节点时:

var parent = document.getElementById('parent');
parent.removeChild(parent.children[0]);
parent.removeChild(parent.children[1]); // <-- 浏览器报错

浏览器报错:parent.children[1]不是一个有效的节点。原因就在于,当<p>First</p>节点被删除后,parent.children的节点数量已经从2变为了1,索引[1]已经不存在了。

因此,删除多个节点时,要注意children属性时刻都在变化。

操作表单
https://www.liaoxuefeng.com/wiki/1022910821149312/1023022460826944

10/28 操作表单

1.例如,很多登录表单希望用户输入用户名和口令,但是,安全考虑,提交表单时不传输明文口令,而是口令的MD5。

普通JavaScript开发人员会直接修改:

<!-- HTML -->
<form id="login-form" method="post" onsubmit="return checkForm()">
    <input type="text" id="username" name="username">
    <input type="password" id="password" name="password">
    <button type="submit">Submit</button>
</form>

<script>
function checkForm() {
    var pwd = document.getElementById('password');
    // 把用户输入的明文变为MD5:
    pwd.value = toMD5(pwd.value);
    // 继续下一步:
    return true;
}
</script>

这个做法看上去没啥问题,但用户输入了口令提交时,口令框的显示会突然从几个变成32个(因为MD5有32个字符)。
要想不改变用户的输入,可以利用<input type="hidden">实现:

<!-- HTML -->
<form id="login-form" method="post" onsubmit="return checkForm()">
    <input type="text" id="username" name="username">
    <input type="password" id="input-password">
    <input type="hidden" id="md5-password" name="password">
    <button type="submit">Submit</button>
</form>

<script>
function checkForm() {
    var input_pwd = document.getElementById('input-password');
    var md5_pwd = document.getElementById('md5-password');
    // 把用户输入的明文变为MD5:
    md5_pwd.value = toMD5(input_pwd.value);
    // 继续下一步:
    return true;
}
</script>

注意到id为md5-password的<input>标记了name="password",而用户输入的id为input-password<input>没有name属性。没有name属性的<input>的数据不会被提交。

2.同源策略和跨域解决方案

https://www.cnblogs.com/rain-chenwei/p/9520240.html

阮一峰:
http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html

3.Promise(还没搞懂,只是知道原理,不知道代码怎么编写)

https://www.liaoxuefeng.com/wiki/1022910821149312/1023024413276544

11/4

1.innerHTML和innerText区别

innerHTML指的是从对象的起始位置到终止位置的全部内容,包括Html标签。
innerText 指的是从起始位置到终止位置的内容,但它去除Html标签。
同时,innerHTML 是所有浏览器都支持的,innerText 是IE浏览器和chrome 浏览器支持的,Firefox浏览器不支持。其实,innerHTML 是W3C 组织规定的属性;而innerText 属性是IE浏览器自己的属性,不过后来的浏览器部分实现这个属性罢了。

扩展:outerHTML指的是除了包含innerHTML的全部内容外, 还包含对象标签本身。

11/13

1.Window getComputedStyle() 方法和 style 的异同

getComputedStyle 和 element.style 的相同点就是二者返回的都是 CSSStyleDeclaration 对象,取相应属性值得时候都是采用的 CSS 驼峰式写法,均需要注意 float 属性。

而不同点就是:
element.style 读取的只是元素的内联样式,即写在元素的 style 属性上的样式;而 getComputedStyle 读取的样式是最终样式,包括了内联样式、嵌入样式和外部样式。
element.style 既支持读也支持写,我们通过 element.style 即可改写元素的样式。而 getComputedStyle 仅支持读并不支持写入。
我们可以通过使用 getComputedStyle 读取样式,通过 element.style 修改样式。

2.js中写css为什么要用驼峰写法?

因为防止和js中的减号冲突

11/20

1.on() 和 click() 的区别:

二者在绑定静态控件时没有区别,但是如果面对动态产生的控件,只有 on() 能成功的绑定到动态控件中。

11/21

1.vue中v-if和v-show

v-show通过display:none;隐藏,如果只是单控制一个元素的隐藏推荐用。
v-if通过直接在dom中删除元素来实现隐藏效果,控制多个元素时使用。

11/22

1.jquery版本

只有1.x版本支持ie8;
只有3.x支持Promise()

12/7

1.移动端适配,github项目–hotcss

2.vue-awesome-swiper

vue轮播图框架
https://segmentfault.com/a/1190000014609379

3.文本不换行,多余的内容显示…

white-space: nowrap; //文本不换行
text-overflow: ellipsis; //超出部分显示…
overflow: hidden; //溢出隐藏
如果要实现第二行多余的文本内容显示…

display: -webkit-box;必须结合的属性,将对象作为弹性伸缩盒子模型显示 。
-webkit-box-orient必须结合的属性,设置或检索伸缩盒对象的子元素的排列方式 。
text-overflow,可以用来多行文本的情况下,用省略号“…”隐藏超出范围的文本 。
https://www.jianshu.com/p/489ac41770a1

12/8

1.display flex和inline-flex的差别

inline-flex会把原来是block的父级元素变为inline,在sass语法里可以这样用
display:if(flex == block,flex,inline-flex);

12/10

1.重新认识flex(各种属性)

https://www.runoob.com/w3cnote/flex-grammar.html

12/14

1.b站全灰的实现,只需要使用一行代码,css滤镜

filter: grayscale(100%);

12/17

1.es6数组新方法String.prototype.padStart(maxLength,“填充的内容”);

举例调用:
var today = “2019”;
var today = today.padStart(5,“a”); // a2019

12/30

1.解决box-shadow效果被相邻元素遮盖问题

使用z-index
结果无效!
因为z-index仅对于定位元素有效! 比如position:relative;这些!

2020/3/25

1.mysql如何批量更新数据?

例子:UPDATE test SET num = CASE id WHEN 1 THEN 3 WHEN 2 THEN 3 WHEN 3 THEN 3 END WHERE id IN (1,2,3)
踩到的坑:id IN (‘1,2,3’)无效,不能是字符串。可以用数组的形式解决
https://www.jianshu.com/p/f71c88aca6fc

4/12

1.vuex的一个问题

在开发毕设项目时使用到了vuex,问题为:在页面刷新后,vuex数据丢失。
原因:vuex数据保存在运行内存中,刷新页面导致重新载入实例,store中数据丢失。

4/14

1.父元素设置display:flex之后,子元素高度被拉伸,而不能自适应的问题

设置align-items:flex-start;

4/17

1.在使用sessionStorage传递对象时,需要对对象进行JSON.stringify()转换,在获取时JSON.parse()换回来

4/18

1.vuecli3的一些个人习惯

vue create 项目名 之后,自定义配置,eslint选择standard。进vue ui,eslint校验设置为“强烈推荐”。

4/20

1.遇到一个.gitignore使用时的问题

问题描述:项目中某个文件已经被Commit过,后面想在.gitignore中忽略,发现不行,原因是.gitignore无法忽略被追踪的文件(也就是被Commit过的)。
解决方法:先git rm --cached对应的文件(移除追踪),再git -am commit “任意内容” 就好了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值