mamba


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 日程表

用原生JavaScript实现类jQuery效果

发表于 2019-02-15

这篇文章将用原生JS实现jQuery的元素选择、添加类、获取/设置文字这几个方法,当然jQuery的实际实现比这个要复杂的多,本文不做深究。

1、实现元素选择

  • 首先我们需要认识到的是,jQuery实际上就是一个函数,所以最基本的结构应该是这样的:
1
2
3
windos.jQuery = function(){

}

window.$ = window.jQuery // 实现类jQuery写法 $()

  • 其次我们需要判断通过该函数传进来的参数是选择器还是DOM节点:是选择器的话,需要使用document.querySelectorAll()来获取元素,然后返回一个对象用来后续的操作;如果是DOM节点,为了保持操作的一致,也需要将其变为一个对象;如果先用一个变量获取DOM的集合,再将其以参数传入,也会返回一个对象(var boxes = document.getElementsByTagName(‘div’) ,再将boxes传入,进入最后一个分支)。具体代码如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    window.jQuery = function(nodeOrSelector){
    let nodes = {}
    // 传入参数为选择器
    if (typeof nodeOrSelector === 'string'){
    let temp = document.querySelectorAll(nodeOrSelector)
    for(let i = 0 ; i < temp.length; i++){
    nodes[i] = temp[i]
    }
    nodes['length'] = temp.length

    }
    // 传入参数为节点
    else if(nodeOrSelector instanceof Node){
    nodes={
    0:nodeOrSelector,
    length:1
    }
    }
    // 传入参数为DOM节点集合
    else{
    for(let i = 0;i<nodeOrSelector.length;i++){
    nodes[i] = nodeOrSelector[i]
    }
    nodes['length'] = nodeOrSelector.length
    }

    return nodes
    }

2、实现获取/设置元素的文本

封装一个Text()函数,操作的是转化过的nodes对象,如果不传参,则获取元素的文本,如果传参(参数需要是字符串),设置元素的文本为所传的参数(其中,如果元素是多个节点的集合,那么会为所有的元素都设置文本)。

1
2
3
4
5
6
7
8
9
10
11
12
13
nodes.Text = function(text){
if(text === undefined){
let texts = []
for(let i = 0;i<nodes.length;i++){
texts[i] = nodes[i].textContent
}
return texts
}else{
for(let i = 0;i<nodes.length;i++){
nodes[i].textContent = text
}
}
}

3、实现为元素添加/删除类

封装一个addClass()函数,操作的转化过的nodes对象,传入的参数需要是一个对象,对象的键名是需要操作的类,对象的键值是操作类型(如{‘red’:true,’green’:false} 表示添加类名为red的类,同时删除类名为green的类)

  • 首先使用for in循环遍历传入的参数,然后使用for循环遍历nodes节点,对每一个元素都要进行操作,判断键值是true还是false,前者则添加类,后者则删除类:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    nodes.addClass = function(classes){
    for(let key in classes){
    for(let i = 0;i<nodes.length;i++){
    if(classes[key]){
    nodes[i].classList.add(key)
    }else{
    nodes[i].classList.remove(key)
    }
    }
    }
    }

观察上面的代码,发现关于if判断语句中的两个分支有相似的地方,而只要有相似的代码,就有优化的空间,所以进行下面的优化:

1
2
3
4
5
6
7
8
nodes.addClass = function(classes){
for(let key in classes){
for(let i = 0;i<nodes.length;i++){
let methodName = classes[key] ? 'add' : 'remove'
nodes[i].classList[methodName](key)
}
}
}

4、完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
window.jQuery = function(nodeOrSelector) {
let nodes = {}
if (typeof nodeOrSelector === 'string') {
let temp = document.querySelectorAll(nodeOrSelector)
for (let i = 0; i < temp.length; i++) {
nodes[i] = temp[i]
}
nodes['length'] = temp.length
console.log(1)

} else if (nodeOrSelector instanceof Node) {
nodes = {
0: nodeOrSelector,
length: 1
}
console.log(2)
} else {
for (let i = 0; i < nodeOrSelector.length; i++) {
nodes[i] = nodeOrSelector[i]
}
nodes['length'] = nodeOrSelector.length
console.log(3)
}

nodes.Text = function(text) {
if (text === undefined) {
let texts = []
for (let i = 0; i < nodes.length; i++) {
texts[i] = nodes[i].textContent
}
return texts
} else {
for (let i = 0; i < nodes.length; i++) {
nodes[i].textContent = text
}
}
}
nodes.addClass = function(classes) {
for (let key in classes) {
for (let i = 0; i < nodes.length; i++) {
let methodName = classes[key] ? 'add' : 'remove'
nodes[i].classList[methodName](key)
}
}
}


return nodes
}

window.$ =jQuery

初识原型与原型链

发表于 2019-02-03

前言:原型与原型链两个名词对于大部分的前端初学者来说,对这两个概念很模糊,无从入手,而且还可能会一脸懵逼。本文将从全局对象引入,分别介绍Number、Boolean、String、Object四个对象,然后再详述原型的相关知识。

1、全局对象 window

首先我们还是先不说原型,先了解一下全局对象以及其中的window对象。

全局对象(global object):

通俗的说就是在脚本的任意地方都可以调用的对象,在浏览器中即window对象。

window对象中:

如alert()方法可以写成window.alert(),或者省去window,直接写做alert()。window对象分两种:

  • 一种是ECMAScript规定必须得有的,比如parseInt 、 parseFloat,以及Number() 、Boolean() 、String() 、Object() 等。
  • 第二种是各个浏览器有的,因为没有写如标准,所以这些方法在各个浏览器的表现形式都有所不同,比如:alert() 、prompt() 、confirm() 、console() 等方法以及 document 、history等对象。

    2、简单类型与对象

  • 众所周知,如果我们想声明一个数值类型的变量,可以这么写:var n1 = 1;,这种写法声明的是一个简单类型;或者这么写:var n2 = new Number(1); ,这种写法下,n2 其实是对象,可以通过n2.valueOf()来获取该对象的值,可以用n2.toString();方法将其转换成字符串。

  • 这两种写法所声明的变量最大的不同是,他们在内存中的存储方式是不同的,n1直接声明,那么他就是简单的数据类型,存储在stack内存中,而n2则是声明了一个对象,stack内存中存储着该对象的地址,对象的内容存储在heap内存中。

  • 但是日常我们没有用n2的写法,依然可以使用valueOf() 方法 和toSting()方法,既然有简单的方法也可以不影响日常操作,为什么还要创建n1这种复杂的写法呢?很显然这里就隐藏着一段JS的黑历史(为了让JavaScript看起来更像Java),这里不细说,有兴趣的同学可以自行Google。

  • 那么JS如何做到使用n1的写法还可以达到n2的用法呢?那是因为如果你调用了如valueOf方法,那么JS会创建一个临时对象,然后调用该对象的方法,调用结束临时对象就被垃圾回收。那么接下来介绍一个易错点在:

  • var num = 1; num.xxx = 2; 这句话会报错吗?答案是不会,后一句为num添加属性时,JS已经为其创建了一个临时对象,为对象添加属性怎么会报错呢?
    然后如果想读取xxx这个属性num.xxx,结果是undefined,因为为num添加完属性后,临时对象就被垃圾回收,num本质上还是一个数值,而数值本身是没有xxx这个属性的(有个这属性的对象已经被删了)。

3、Number对象

Number的常用属性有:

  • Number.valueOf(); 获取对象本身的值
  • Number.toString(); 将数值转化为字符串

    注:number类型的的toString方法可以加参数,表示按照什么进制来解析(不加参数默认按十进制解析),如:
    1
    2
    3
    (100).toString(); // "100"
    (100).toString(2); // "1100100"
    100..toString(16); // "64"
  • Number.toFixed(); 将其转换为小数,
    如:2..toFixed(3); // “2.000”

  • Number.toExponential(); 将其转化为科学计数法,
    如:200..toExponential(); // “2e+2”

4、String对象

String的常用属性有:

  • String.charAt(); 获取字符串中某一位的字符
    如:”hello world”.charAt(1); // “e”

  • String.charCodeAt(); 获取字符串中某一位的字符的Unicode编码
    如:”hello world”.charCodeAt(1); // 101

  • String.trim(); 删除字符串中多余的空格
    如:’ hello world ‘.trim(); // “hello world”

  • String1.concat(String2); 连接字符串1和字符串2
    如:”hello”.concat(“world”); // “helloworld”

-String.slice(start,end); 截取字符串,从start到end
如:”hello world”.slice(3,5); // “lo”

  • String.replace('xx','yy'); 将字符串中的xx替换成yy(只能替换第一次出现的字符)
    如:”hello world”.replace(‘o’,’a’) // “hella world”

  • String.indexOf(); 搜索字符串中的内容(只检测到第一次出现的字符),没搜到返回-1
    如:”hello world”.indexOf(‘o’); // 4
    “hello world”.indexOf(‘p’); // -1

  • String.split(); 分隔
    如:”hello world”.split(‘ ‘); // [“hello”, “world”]

  • String.includes(xx); 检查字符串中是否包含xx
    如:”hello world”.includes(‘aa’) ; //false

  • String.substr(start,len); 截取
    如:”hello world”.substr(3,2); // “lo”

5、Boolean对象

这里要介绍一个两种不同的赋值方法下容易出错的地方。

1
2
3
4
5
var b1 = false;
var b2 = new Boolean(false);
if(b1) { console.log('b1') } ;
if(b2) { console.log('b2') } ;
///打印结果为 b2

上述代码之中,b1和b2的值都是false,而使用if判断语句,却将b2转化为了true,那是因为b2本身的数据类型是对象,而对象不论是否是空对象,转化成boolean都是true。

6、Object对象

Object对象,两种赋值方法是一样的,没有任何区别。
不过需要注意的是:

1
2
3
var obj1 = {};
var obj2 = {};
obj1 === obj2; // false

上面的代码中,同样都是空数组,但是他们的值是不相等的,因为对象在stack内存中存储的只是其内容在heap内存中的地址,而每个对象的内容在heap内存中的地址是不会一样的,所以对象与对象一般都是不相等的。

7、公有属性 / 原型

那么说了这么多,接下来即将引入原型这个概念。
上面说了四种对象,而他们都有相同的一些属性,比如valueOf,toString等,难道JS每次声明一个对象都要写一次这些方法嘛?答案是否定的,因为真要这么写的话会非常占用内存,所以实际上JS引擎是这么做的:把所有的对象共有的属性全部放在heap内存的一个地方,当需要使用的时候,引用这个对象即可,这么操作就会减少不必要的内存浪费,而这个公有属性,就是传说中的原型。

  • __proto__就是这些公有属性的引用。
  • Object.__proto__指向的就是对象的公有属性。
  • 而比如Number对象、String对象、Boolean对象,他们还有各自独有的公用属性,他们的__proto__指向的就是各自的独有属性,这些独有属性的__proto__指向对象的公有属性。
    由此可知obj1.toString === obj2.toString这句话是正确的。
图中红色的线,就组成了一条原型链。

JS引擎一出来就会把上图的这棵树画好让各个对象来引用。而浏览器一开始就有了原型对象,用Object.prototype指向原型,防止其因为没有对象引用而被回收。
则出现了一些等式:

  • obj.__proto__ === Object.prototype即对象的__proto__指向Object对象的prototype
  • num.__proto__ === Number.prototype 即数值的__proto__指向Number对象的共有属性
  • num.__proto__.__proto__ === Object.prototype即Number对象的公有属性的__proto__指向Object对象的公有属性。
  • 其他如Boolean对象、String对象和Number对象同理。

那么__proto__与pprototype到底代表着什么,有什么作用,区别是什么呢,请看下文。

8、proto 与 prototype

上图是我们还没有写代码的时候,浏览器内存中已经构建好了的内容。

由图可知,prototype是浏览器本身就写好的。
当我们写了一句代码var s = new String(‘ hello ‘) 以后:
我们创建的对象的__proto__会用来指向原有的String对象,使得我们可以调用String对象的公有属性。

于是我们有了以下结论:

proto 是某对象公用属性的引用,是为了用户使用其共用属性中的方法而存在的 。
prototype 是浏览器写的,本身就存在,是某对象的共同属性的引用,为了不让对象的公用属性因没有被调用而被垃圾回收而存在。

9、一些烧脑的等式

众所周知,var 对象 = new 函数;是JS中很基本的语句,如var str = new String('aaa'),因此就有了下面这句等式:

  • 对象.__proto__ === 函数.prototype ,即对象的__proto__指向了构造这个对象的函数的prototype。 因此我们还知道了__proto__ 是对象我的属性,prototype是函数的属性。

于是我们可以根据上述等式做一些推论:

  • 首先我们知道,函数的prototype是对象,这个对象对应的就是最简单的函数Object,所以替换的到下面的式子:
    函数.prototype.__proto__ === Object.prototype

  • 由于函数本身即是函数(最优先视为函数),也是对象,而函数的构造函数是Function ,所以替换得:
    函数.__proto__ === Function.prototype

  • Function即是对象,也是函数,但他优先是个函数,所以将Function替换上面的式子,则变为:
    Function.__proto__ === Function.prototype

  • 而Function.prototype也是对象,是普通的对象,所以其对应的函数使Object,替换得:
    Funciton.prototype.__proto__=== Object.prototype

10、神奇的Function

根据上面的各种推论,我们发现了其中最神奇的东西,Function,他即是函数,也是对象,所以他有函数的prorotype,也有对象的__proto__,所以出现了下图的情况:

即Function.prototype 与Funciton.__proto__互相引用。
另外需要注意的一点:
Object.__proto__ === Function.prototype,因为 Function 是 Object 的构造函数。

JS里的数据类型转换

发表于 2019-01-30

如何将其他的数据类型转换成String字符串类型?

注:下面介绍的三种转换成字符串的方法都不适用于对象object,所得结果都是”[object Object]”,但是对于函数和数组是可以转换的

  • ?.toString()
  • String(?)

  • ‘’+ ?

    上面的问号?代表是其他数据类型,这样可以快速得知使用方法

方法1:?.toString()

toString方法适用于number类型和boolean类型,但是将数字转换为字符串时,要特别注意,如

1
2
3
4
5
//将数字转换为字符串错误写法
1.toString() //Uncaught SyntaxError: Invalid or unexpected token
//正确写法
(1).toString() //"1"
true.toString() //"true"

但是对于null和undefined,使用这种方法会报错,如:
报错信息揭秘:
不能读null的toString方法,null没有toString的api,所以报错,undefined类似

1
2
null.toString() //Uncaught TypeError: Cannot read property 'toString' of null
undefined.toString() //Uncaught TypeError: Cannot read property 'toString' of undefined

对于object,toSring方法结果不正确,结果永远是”[object Object]”,函数和数组是比较特别,具体请看代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//错误写法
{}.toString() //Uncaught SyntaxError: Unexpected token .
//正确写法
({}).toString() //"[object Object]"
var obj = {}
obj.toString() //结果为 "[object Object]"

//函数
//错误写法
function(){}.toString() //Uncaught SyntaxError: Unexpected token (
//正确写法
(function(){}).toString() //"function(){}"

//数组
[].toString() //""
[1,2,3].toString() //"1,2,3"

方法2:String(?)

String() 该方法适用于所有数据类型(除了对象,结果同toString())

1
2
3
4
5
6
7
String(123)  //"123"
String(true) //"true"
String(null) //"null"
String(undefined) //"undefined"
String({name:'frank'}) //"[object Object]"
String([1,2,3]) //"1,2,3"
String(function(){}) //"function(){}"

方法3:’’+ ?(老司机做法)

注:这种方法的原理是:‘+’ 运算符只能相加相同的数据类型,如果两边的数据类型不同,他会优先将其转换成字符串来相加。
‘’+ ? 即使用+运算符加上空字符串(同样对object无效)该方法适用于所有数据类型(除了对象,结果同toString())

1
2
3
4
5
6
7
8
9
10
11
12
13
1 + ''   //"1"
false + '' //"false"
null + '' //"null"
undefined + '' //"undefined"
[1,2,3] + '' //"1,2,3"

var obj = {name : 'frank'}
obj + '' //"[object Object]"

function(){} + '' //Uncaught SyntaxError: Unexpected token (
(function(){}) + '' //"function(){}"

[1,2,3] + '' //"1,2,3"

踩坑时间到

小谷问:1+’1’的结果是多少?
小白说:你这是看不起我吗?2啊
小谷说:你踩坑啦,当然不是,具体,我一一给你道来

1
2
3
1+'1'  //"11"
//上面的做法,相当于是下面这个步骤,根据上面的那个原理得出
(1).toString() + '1' //"11"

小Tips:

  • 三种转换成字符串的方法对于对象来说,是特殊的
  • 使用这种方法?.toString()报错,只需给需要转换的东西加上个括号,具体例子请看上面

如何将其他的数据类型转换成Number数值类型?

注:parseInt逐个解析字符,而Number函数整体转换字符串的类型。parseInt,parseFloat和Number函数都会自动过滤一个字符串前导和后缀的空格。parseInt(?,?)说明parseInt可以有两个参数

  • Number(?)
  • parseInt(?,?)
  • parseFloat(?)
  • ‘?’ - 0
  • ‘?’ +

方法1:Number(?)

使用Number函数,可以将任意类型的值转化成数值。Number函数将字符串转为数值,要比parseInt函数严格很多。基本上,只要有一个字符无法转成数值,整个字符串就会被转为NaN。
下面分成两种情况讨论,一种是参数是原始类型的值,另一种是参数是对象。

(1)原始类型值

示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 数值:转换后还是原来的值
Number(324) // 324

// 字符串:如果可以被解析为数值,则转换为相应的数值
Number('324') // 324

// 字符串:如果不可以被解析为数值,返回 NaN
Number('324abc') // NaN

// 空字符串转为0
Number('') // 0

// 布尔值:true 转成 1,false 转成 0
Number(true) // 1
Number(false) // 0

// undefined:转成 NaN
Number(undefined) // NaN

// null:转成0
Number(null) // 0

Number('42 cats') // NaN

(2)对象

Number方法的参数是对象时,将返回NaN,除非是包含单个数值的数组。
示例如下:

1
2
3
Number({a: 1}) // NaN
Number([1, 2, 3]) // NaN
Number([5]) // 5

Number背后的转换规则比较复杂。
第一步,调用对象自身的valueOf方法。如果返回原始类型的值,则直接对该值使用Number函数,不再进行后续步骤。
第二步,如果valueOf方法返回的还是对象,则改为调用对象自身的toString方法。如果toString方法返回原始类型的值,则对该值使用Number函数,不再进行后续步骤。
第三步,如果toString方法返回的是对象,就报错。

方法2:parseInt()

parseInt方法用于将字符串转为整数。
如:parseInt(‘123’) // 123
如果字符串头部有空格,空格会被自动去除。parseInt(‘ 81’) // 81
如果parseInt的参数不是字符串,则会先转为字符串再转换。

1
2
3
parseInt(1.23) // 1
// 等同于
parseInt('1.23') // 1

字符串转为整数的时候,是一个个字符依次转换,如果遇到不能转为数字的字符,就不再进行下去,返回已经转好的部分。

1
2
3
parseInt('8a') // 8
parseInt('15e2') // 15
parseInt('15px') // 15

如果字符串的第一个字符不能转化为数字(后面跟着数字的正负号除外),返回NaN。

1
2
3
4
5
parseInt('abc') // NaN
parseInt('.3') // NaN
parseInt('') // NaN
parseInt('+') // NaN
parseInt('+1') // 1

注:parseInt()方法默认转换成十进制,不过需要注意的是,如果参数本身就是number类型,且是0x开头(16进制),或0o开头(八进制),0b开头(二进制),0开头且后面的数字没有8和9(视为八进制),那么parseInt方法会将其以相应的进制转换成十进制展示出来。

1
2
3
parseInt(0o377)  //255
parseInt(0xff) //255
parseInt(0b11) //3

如果字符串以0x或0X开头,parseInt会将其按照十六进制数解析。

1
parseInt('0x10') // 16

如果字符串以0开头,将其按照10进制解析。

1
parseInt('011') // 11

因此,为了防止意外解析成其他进制,建议添加第二个参数按照特定进制解析:如:
如果第二个参数不是数值,会被自动转为一个整数。这个整数只有在2到36之间,才能得到有意义的结果,超出这个范围,则返回NaN。如果第二个参数是0、undefined和null,则直接忽略。

1
2
3
4
5
6
7
8
parseInt('1000', 2) // 8
parseInt('1000', 6) // 216
parseInt('1000', 8) // 512
parseInt('10', 37) // NaN
parseInt('10', 1) // NaN
parseInt('10', 0) // 10
parseInt('10', null) // 10
parseInt('10', undefined) // 10

方法3:parseFloat()

parseFloat方法用于将一个字符串转为浮点数。

1
parseFloat('3.14') // 3.14

如果字符串符合科学计数法,则会进行相应的转换。

1
2
parseFloat('314e-2') // 3.14
parseFloat('0.0314E+2') // 3.14

如果字符串包含不能转为浮点数的字符,则不再进行往后转换,返回已经转好的部分。

1
parseFloat('3.14more non-digit characters') // 3.14

parseFloat与Number的对比

1
2
3
4
5
6
7
8
9
10
11
parseFloat(true)  // NaN
Number(true) // 1

parseFloat(null) // NaN
Number(null) // 0

parseFloat('') // NaN
Number('') // 0

parseFloat('123.45#') // 123.45
Number('123.45#') // NaN

方法4:’?’ - 0

字符串 - 0。如:’11’ - 0 //11`

方法5: + ‘?’

另辟蹊径的方法: + 字符串,这里的+并不是取正值的意思,负数一样可行,如:+ ‘-011’ ; // -11
注:上面介绍的使用运算符的转换方法,字符串不能有除了数字外的其他字符(正负号,表示进制的标识除外),字符串中,0b、0x、0o开头会以对应表示的进制解析,如果是’011’,则会解析成十进制而不是二进制,小数同样可以用这两种方法进行转换
小Tips:

  • Number方法基本上,只要有一个字符无法转成数值,整个字符串就会被转为NaN
  • parseInt方法为了防止意外解析成其他进制,建议添加第二个参数按照特定进制解析
  • parseFloat会将空字符串转为NaN

如何将其他数据类型转换为Boolean布尔类型?

小白做法:Boolean() ,如:

1
2
Boolean(1)  //true
Boolean({}) //true

老司机做法:双重取反:!! x ,如:

1
2
3
!true //false
!!true //true
!!1 //true

五个falsy值,即转换成Boolean后为false的值:
0 、 NaN 、 null 、 undefined 、‘’(空字符串)

JS中的数据在内存中的存储方式

什么是内存呢,举个例子:

你买一个8G 的内存条,操作系统开机即占用 512MB,Chrome 打开即占用 1G 内存, Chrome 各每个网页分配一定数量的内存, 这些内存要分给页面渲染器、网络模块、浏览器外壳和 JS 引擎(V8引擎)。
JS 引擎将内存分为代码区和数据区, 我们只研究数据区。 数据区分为 Stack(栈内存)和 Heap(堆内存)。 简单类型的数据(如Number,string等)直接存在 Stack 里, 复杂类型的数据(object对象)是把 Heap 地址存在 Stack 里。

对象存储数据的方式

对象的存储方式是在stack栈内存存储一个地址,形成对对象的引用,地址指向heap堆内存的某个位置,object是对象的引用。它在stack栈内存存的是地址,而不像其他数据类型直接把内容存在stack栈内存

垃圾回收

JS中的垃圾回收机制:如果一个对象没有被引用,它就是垃圾,将会被回收。(时间并不确定的)
内存泄漏就和垃圾回收机制有一定的联系:由于浏览器的一些bug,使得本应被被标记为垃圾的数据没有被标记,而这些垃圾数据占用的内存将永远被占用,哪怕你把当前页面关掉都不会被释放,除非直接关掉整个浏览器。(IE6就有此类bug)
解决方法:(但是要抵制IE低版本浏览器)

1
2
3
4
window.onunload = function(){
//需要将所有的事件监听为null,十分麻烦
document.body.onclick = null;
}

四个关于内存的题目

画内存图(解决问题的方法),只要你不画图,就会错,以不变应万变

题目一:

1
2
3
4
5
6
var a = 1;
var b = a;
b =2;
//请问 a 是什么?
//a = ? => a === 1
使用内存图解决思路:

题目二:

1
2
3
4
5
var a = {name:'a'};
var b = a;
b = {name: 'b'};
//请问 a.name 是什么?
//a.name === " a"

题目三:

1
2
3
4
5
var a = {name: 'a'};
var b = a;
b.name = 'b';
//请问 a.name 是什么?
//a.name === " b"

题目四:

1
2
3
4
5
var a = {name:'a'};
var b = a;
b = null;
//请问 a是什么?
//a === {name: 'a'}

面试题

1
2
3
4
5
var a = {n:1};  
var b = a;
a.x = a = {n:2};
alert(a.x);
alert(b.x);

坑爹的面试题,a.x = a = {n:2};这句话是什么意思?浏览器看到这句话,就会问a是什么?
浏览器读到a.x = a = {n:2};这句话时,先看左面,再往右边看

1
2
3
a.x = a = {n:2}
//a = {n:2},此时的a指向新的地址
//a.x还是之前的地址,并没有发生改变

注:这些箭头并不是真实存在的,只是虚拟的示意

深拷贝与浅拷贝

深拷贝:
var a = 1;
var b = a;
b = 2 ;
//请问a是什么?

a的值仍然还是1。
即 b 变而不影响 a,就叫深拷贝(简单数据类型的赋值都是深拷贝)
浅拷贝

1
2
3
var a = {x:1} ;
var b = a ;
b.x = 2;

变而导致 a变,就叫浅拷贝

JS中对象的循环引用

1
2
3
var obj = {name:'frank'} ; 
obj.self = obj;
obj.self.self.self.name; // "'frank"

上述代码就可以实现对象的循环引用,原理是:为对象添加一个属性,属性值就是对象自己(在内存中就是对象引用的地址),从而达到可以通过引用该对象的self属性来引用对象本身。

CSS布局相关内容

发表于 2019-01-12

一、常见布局

1.浮动布局

可以通过盒模型的 float 属性实现浮动布局,使元素脱离文档流,浮动起来。
(1)使所有子元素浮动起来:float: left; 或 float: right;
(2)给父元素添加 clearfix 类清除浮动(加在类名后面,与类名用空格隔开),例:

1
2
3
<div class="picture clearfix">
······
<div>

(3)在 css 文件中添加以下代码:

1
2
3
4
5
.clearfix::after{           
content: '';
display: block;
clear: both;
}

2.定位布局

通过 position 属性来进行定位,确定盒模型的位置。

  • relative 定位:相对定位
    相对元素的定位是相对其正常位置。
    1
    2
    3
    4
    h2.pos-left{
    position: relative;
    left: -20px;
    }

相对定位元素经常被用来作为绝对定位元素的容器块。

  • absolute 定位:绝对定位
    绝对定位的元素的位置相对于最近的已定位父元素,若没有已定位的父元素,那么它的位置相对于html.
    1
    2
    3
    4
    5
    h2{
    position: absolute;
    left: 100px;
    top: 150px;
    }

(1)脱离文档流:使元素的位置与文档流无关,因此不占据空间。
(2)absolute 定位的元素和其它元素重叠。

二、两种布局的应用

1.左右布局

  • float+margin
    一个父 div 包裹两个子 div,并用 float 属性使两个子 div 浮动起来;子 div 宽度可以设置为固定值如 width: 100px;,也可以写为父 div 宽度的百分比形式如 width: 50%;。可以给两个子 div 设置一定的 margin 值,确定其间距。
    position 绝对定位
    父 div 用 position: relative; 相对定位,两个子 div 用 position: absolute; 使其对父元素绝对定位,并用 left 或 right 调整其左右位置。

2.左中右布局

  • float+margin
    一个父 div 包裹三个子 div,并用 float 属性使三个子 div 都浮动起来;可以设置其中两个子 div 的宽度,使第三个宽度自适应;也可以根据需要分别设置三个子 div 的宽度。再用 margin 调整位置。
    position 绝对定位
    与左右布局类似,父 div 用 position: relative; 相对定位,三个子 div 用 position: absolute; 绝对定位,并根据需要用 left 或 right 调整其左右位置。

三、设置居中

1.垂直居中

  • 内联元素垂直居中:使 height 和 line-height 高度相同:
    1
    2
    3
    4
    .a{
    height: 50px;
    line-height: 50px;
    }

块状元素垂直居中:用 position 定位实现:

1
2
3
4
5
6
7
8
9
10
.parent{
height: 200px;
position: relative;
}
.a{
heihght: 1000px;
margin-top: -50px;
position: absolute;
top: 50%;
}

2.水平居中

  • 内联元素水平居中:通过父元素实现:

    1
    2
    3
    .parent{
    text-align: center;
    }
  • 块状元素水平居中:宽度确定,用 margin 属性:

    1
    2
    3
    a.{
    margin: 0 auto;
    }
  • 宽度未知或多个块状元素水平居中:

    1
    2
    3
    4
    5
    6
    .parent{
    text-align: center;
    }
    .a{
    display: inline-block;
    }

其他小技巧

  1. 若外面父 div 高度是固定的,里面的子 div 高度可写为 height:100%;
  2. 实现图片水平垂直局中且自适应的方法

    1
    2
    background-position: center center;
    background-size: cover;
  3. 鼠标悬停出现下划线并且不会晃动的方法

    1
    2
    3
    4
    5
    6
    border-bottom: 1px solid transparent;
    }
    /*增加悬浮效果-下划线*/
    .topNavBar>nav>ul>li>a:hover{
    border-bottom: 1px solid red;
    }
  4. CSS 中相同的代码段,可以通过 “inherit” 继承关系,使其继承父元素的定义:

    1
    2
    3
    4
    5
    6
    div{
    color: red;
    }
    div a{
    color: inherit;
    }
  5. content-box 与 border-box 的区别:

  • content-box 的 width 不包括 padding 和 border.

  • border-box 的 width 包括 padding 和 border.

  1. 给 div 加 padding 会使 div 变大,所以应该把 div 里面的内容另外打包成一个新的 div,然后给它加 padding.
  2. 若 li 标签不能包裹住 a 标签,给 a 标签一个 display:block 就可以消除 bug。
  3. 解决距离边框的上下距离不一致的解决办法.
    1
    vertical-align: top;

HTML常用的几个标签

发表于 2019-01-06

1.iframe

HTML内联框架元素iframe标签表示嵌套的浏览上下文,有效的将另一个HTML页面嵌入到当前页面中

属性

frameborder

  • 取值为1时(默认值),告诉浏览器在当前iframe与其他iframe之间绘制边框,取0时则无需绘制
    name
  • 嵌入的浏览上下文(框架)的名称。该名称可以用作a标签,form标签的target属性值,或input 标签和button标签的formtaget属性值。
    height
  • 以CSS像素格式HTML 4.01,或像素格式HTML5,或百分比格式指定frame的高度。
    width
  • 以CSS像素格式HTML 4.01,或像素格式HTML5,或百分比格式指定frame的宽度。
    src
  • 嵌套页面的URL地址。使用遵守同源策略的 ‘about:blank’ 来嵌套空白页。

    2.a标签

    HTML中a(或锚元素) 可以创建一个到其他网页、文件、同一页面内的位置、电子邮件地址或任何其他URL的超链接。

    属性

    1.href

    没有href属性
  • 这是没有意义,本身和span没有区别。
    href=”qq.com”
  • 打开的是一个相对文件路径,并不会打开网址。
    href=”http://qq.com"
  • 打开是一个网址。
    href=”//qq.com”
  • 这是无协议,将会自动识别并继承在它之前的协议。若是直接打开,识别的是file协议,若是指定了协议,例如localhost:8080//qq.com,则会识别http协议,从而打开qq网址。
    href=”javascript:;”
  • 这是伪协议,这种写法常用于一些奇怪的需求。
    不使用href,就点击不了。
    使用href=””则会刷新本页面。
    使用href=”#”产生锚点,会调到页面最上端。
    使用href=”javascript:;”,不产生任何动作。
    使用href=”javascript:alert(1)”,是存在可执行的。
    href=”?name=mamamoo”
  • 这种情况下表示会在url后面添加?name=mamamoo并且发起了一个get请求。
    href=”#xxx”
  • 在url后面添加#xxx添加一个锚点,并不会发起请求。

    2.target

    “_blank”
  • 新开一个空白页,打开网址。
    “_self”
  • 在自身窗口打开网址。
    “_parent”
  • 在父级窗口打开网址。
    “_top”
  • 在最上级窗口打开网址。
    如:我用iframe在A窗口嵌套了B窗口,在B窗口中嵌套C窗口。那么C窗口的父级指的是B,最上级指的是A。

    3.download

  • 可实现下载操作

    3.input标签

    HTML中input元素用于为基于Web的表单创建交互式控件,以便接受来自用户的数据。

    属性

    1.name

  • 控件的名称,与表单数据一起提交。

    2.value

    控件的初始值. 此属性是可选的,除非type 属性是radio或checkbox。注意,当重新加载页面时,如果在重新加载之前更改了值,Gecko和IE将忽略HTML源代码中指定的值。

    3.checked

  • 如果该元素的type属性的值为radio或者checkbox,则该布尔属性的存在与否表明了该控件是否是默认选择状态.

    4.disabled

  • 这个布尔属性表示此表单控件不可用。

    5.type

    button
  • 无缺省行为按钮。
    checkbox
  • 复选框。必须使用 value 属性定义此控件被提交时的值。使用 checked 属性指示控件是否被选择。
    password
  • 一个值被遮盖的单行文本字段。使用 maxlength 指定可以输入的值的最大长度 。
    radio
  • 单选按钮。必须使用 value 属性定义此控件被提交时的值。使用checked 必须指示控件是否缺省被选择。在同一个”单选按钮组“中,所有单选按钮的 name 属性使用同一个值; 一个单选按钮组中是,同一时间只有一个单选按钮可以被选择。
    submit
  • 用于提交表单的按钮。
    text
  • 默认的类型,单行字段;换行会将自动从输入的值中移除。

    4.form标签

    HTML form 元素 表示了文档中的一个区域,这个区域包含有交互控制元件,用来向web服务器提交信息。

    属性

    1.action

  • 一个处理这个form信息的程序所在的URL。这个值可以被 button或者 input 元素中的 formaction 属性重载(覆盖)。

    2.method

  • a标签和form标签都是跳转页面,不过a标签是get请求,form是post请求。get是获取内容,post是提交内容。

    3.name

  • 这个form的名字。标签要有name属性,不然是无法在请求中得到。

    4.target

  • form标签的target属性和a标签是一样。

    5.button标签

    HTML中button 元素表示一个可点击的按钮,可以用在表单或文档其它需要使用简单标准按钮的地方。
    属性

    1.name

  • button的名称,与表单数据一起提交。

    2.value

  • button的初始值。它定义的值与表单数据的提交按钮相关联。当表单中的数据被提交时,这个值便以参数的形式被递送至服务器。

    3.type

    submit
  • 此按钮提交表单数据给服务器。
    reset
  • 此按钮重置所有组件为初始值。
    button
  • 此按钮没有默认行为。它可以有与元素事件相关的客户端脚本,当事件出现时可触发。
    menu
  • 此按钮打开一个由指定menu元素进行定义的弹出菜单。

    6.select标签

    HTML中select标签表示其提供选项菜单的控制。

    属性

    1. multiple

  • 支持多选。

    2. disabled

  • 选项中标记了disabled是无法被选中。

    3. selected

  • 默认选中的选项。

    7.table标签

    HTML的table标签表示表格数据,即通过二维数据表表示的信息。浏览器会按照thead、tbody、tfoot顺序来进行显示,不按照写的顺序显示

HTML请求相关内容(一)

发表于 2019-01-05
  • W3C简介
  • MDN简介
  • HTML
  • 空标签
  • 可替换标签
  1. W3C简介
  • W3C(World Wide Web Consortium)万维网联盟,又称W3C理事会,是万维网之父 Sir Timothy John Berners-Lee 于1994年成立的。
  • 为解决网络应用中不同平台、技术和开发者带来的不兼容问题,保障网络信息的顺利和完整流通,万维网联盟制定了一系列标准并督促网络应用开发者和内容提供者遵循这些标准。标准的内容包括使用语言的规范,开发中使用的导则和解释引擎的行为等等。W3C也制定了包括XML和CSS等的众多影响深远的标准规范。
  • W3C制定的网络标准似乎并非强制,而只是推荐标准。因此部分网站仍然不能完全实现这些标准,特别是使用早期所见即所得网页编辑软件设计的网页往往会包含大量非标准代码
  • 具体解释请查看维基百科,需FQ哦
    https://zh.wikipedia.org/wiki/%E4%B8%87%E7%BB%B4%E7%BD%91%E8%81%94%E7%9B%9
  1. MDN简介
  • MDN Web Docs(旧称Mozilla Developer Network、Mozilla Developer Center,简称MDN)是一个汇集众多Mozilla基金会产品和网络技术开发文档的免费网站[1]。
  • 具体解释请查看维基百科
    https://zh.wikipedia.org/wiki/MDN_Web_Docs
  • 如需查看MDN官网点击以下链接
    https://developer.mozilla.org/zh-CN/
  1. HTML
  • HTML由一系列的元素(elements)所组成,这些元素可以用来封装、包装或标记内容的不同部分,使其以某种方式显示,或以某种方式执行。
  • 这里列出了所有标准化的 HTML5 元素,该页只列出有效的 HTML5 元素。
  • 根元素
    | 元素 | 元素描述 |
    | html | 代表 HTML 或 XHTML 文档的根。其他所有元素必须是这个元素的子节点 |
  • 查看其他元素请点击链接
    https://developer.mozilla.org/zh-CN/docs/Web/Guide/HTML/HTML5/HTML5_element_list
  1. 空标签
  • 在HTML标签中,尖括号(<>)总是成对存在的,即有开始标签,有结束标签,且绝大部分HTML标签在开始与结束标签之间还可以加入具体内容。
  • 但并不是所有的元素都有开始标签、具体内容以及结束标签的。由于HTML元素的内容是开始标签与结束标签之间的内容。而某些HTML元素具有空内容,那些含有空内容的HTML元素,就是空标签。空标签是在开始标签中关闭的,常见的空标签有:area、base、col、colgroup (when the span is present)、embed、img、input、link、meta、param、source、track、wbr等。

    1
    img src="xxxxx/xxxxx.png" alt="test image"
  • 以上代码中的img包含了两个属性 src 和 alt ,但是它并没有闭合标签,也没含有具体内容,所以它就是我们所说的空标签,作用是向其所在的位置嵌入一个图像。

  1. 可替换标签
  • CSS中,可替换元素(replaced element)的样式展现不是由CSS来控制的。这些元素是一类外观渲染独立于CSS的外部对象。典型的可替换元素有 img、 object、 video 和表单元素textarea、input等 。某些元素只在一些特殊情况下表现为可替换元素,例如 audio 和 canvas 。 通过 CSS的 content 属性来插入的对象被称作匿名可替换元素(anonymous replaced elements)。CSS在某些情况下会对可替换元素做特殊处理,比如计算外边距和一些auto值。
  • 可替换元素就是浏览器根据元素的标签和属性,来决定元素的具体显示内容。
  • 可替换元素一般有内在尺寸和宽高比(auto时起作用),所以具有width和height,可以设定。
    例如你不指定img的width和height时,就按其内在尺寸显示,也就是图片被保存的时候的宽度和高度。
  • 对于表单元素,浏览器也有默认的样式,包括宽度和高度

浅谈HTTP

发表于 2019-01-02

了解HTTP的请求与响应

  1. HTTP的作用就是指导浏览器和服务器如何进行沟通
  • 浏览器负责发起请求
  • 服务器在80端口接受请求
  • 服务器负责返回内容(响应)
  • 浏览器负责下载响应内容
  1. 如何使用curl命令
  • 在命令行中输入curl -s -v -H – “https://www.baidu.com",即可以创造一个请求并得到响应 请求的内容为
    1
    2
    3
    4
    5
    > GET / HTTP/1.1
    > Host: www.baidu.com
    > User-Agent: curl/7.63.0
    > Accept: */*
    >

请求的格式
1 动词 路径 协议/版本
2 Key1: value1
2 Key2: value2
2 Host: www.baidu.com
2 User-Agent: curl/7.63.0
2 Content-Length: 字节
2 Content-Type: application/x-www-form-urlencoded
3
4 要上传的数据

  • 请求最多包含四部分,最少包含三部分。(也就是说第四部分可以为空)
  • 第三部分永远都是一个回车(\n)
  • 动词有 GET POST PUT PATCH DELETE HEAD OPTIONS 等
  • 这里的路径包括「查询参数」,但不包括「锚点」
  • 如果你没有写路径,那么路径默认为 /
  • 第 2 部分中的 Content-Type 标注了第 4 部分的格式
    响应的内容
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    < HTTP/1.1 200 OK
    < Accept-Ranges: bytes
    < Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform
    < Connection: Keep-Alive
    < Content-Length: 2443
    < Content-Type: text/html
    < Date: Wed, 02 Jan 2019 15:28:36 GMT
    < Etag: "58860410-98b"
    < Last-Modified: Mon, 23 Jan 2017 13:24:32 GMT
    < Pragma: no-cache
    < Server: bfe/1.0.8.18
    < Set-Cookie: BDORZ=27315; max-age=86400; domain=.baidu.com; path=/
    <

1 协议/版本号 状态码 状态解释
2 Key1: value1
2 Key2: value2
2 Content-Length: 17931
2 Content-Type: text/html
3
4 要下载的内容

  • 第 2 部分中的 Content-Type 标注了第 4 部分的格式
  • 第 2 部分中的 Content-Type 遵循 MIME 规范
    用 Chrome 发请求
  • 打开 Network
  • 地址栏输入网址
  • 在 Network 点击,查看 request,点击「view source」
  • 点击「view source」 点击「view source」 点击「view source」
  • 终于点了?可以看到请求的前三部分了
  • 如果有请求的第四部分,那么在 FormData 或 Payload 里面可以看到 用 Chrome 查看响应
  • 打开 Network
  • 输入网址
  • 选中第一个响应
  • 查看 Response Headers,点击「view source」,点击「view source」,点击「view source」
  • 你会看到响应的前两部分
  • 查看 Response 或者 Preview,你会看到响应的第 4 部分

git入门

发表于 2018-12-30

介绍 git init,git add,git commit的用法

  1. git init (初始化本地仓库.git)
    a: 当你本地创建了一个工作目录,你可以进入这个目录,使用’git init’命令进行初始化
    b: 例:我们创建一个目录作为项目目录:mkdir git-demo
    c: 进入目录 cd git-demo
    d: git init,这句命令会在git-demo里创建一个.git目录
  1. git add 将文件添加到【暂存区】
    a: 你可以一个一个的add
    git add index.html
    b: 也可以一次性add
    git add . 意思是把当前目录中(.表示当前目录)里面的变动都加到【暂存区】
  2. git commit 用来正式提交变动,提交至.git仓库
    git commit index.html -m ‘添加index.html
1…45

张哲

48 日志
GitHub E-Mail
© 2019 张哲
由 Hexo 强力驱动
|
主题 — NexT.Pisces v5.1.4