大吉大利,今晚学习


  • 首页

  • 标签

  • 归档

this和new和构造函数

发表于 2019-05-18

this的指向

函数的call,apply.bind区别

  1. call():调用一个函数, 其具有一个指定的this值和分别地提供的参数(参数的列表)。
    fun.call(thisArg, arg1, arg2, ...)

  2. apply():调用一个具有给定this值的函数,以及作为一个数组(或类似数组对象)提供的参数。
    fun.apply(thisArg, [argsArray])

    call()方法的作用和 apply() 方法类似,区别就是call()方法接受的是参数列表,而apply()方法接受的是一个参数数组。

  3. bind():(创建)返回一个新函数,并且是函数内部this为传入的第一个参数。
    fun.bind(thisArg[, arg1[, arg2[, ...]]]);
    bind是返回对应的函数,便于稍后调用。apply、call 则是立即调用。

实际中使用函数的this

1
2
3
4
5
6
7
btn.addEventListener('click' ,function handler(){
console.log(this) // 请问这里的 this 是什么
})

$ul.on('click', 'li' , function(){
console.log(this) // 请问这里的 this 又是什么
})

在上面两例中,我们要弄清楚this是什么,应该去看源码,或者看文档,千万不要瞎猜,想当然的认为this指向就是谁。

当一个函数被调用时,拥有它的object会作为this传入。在global下,就是window or global,其他时候就是相应的object。

出现在函数嵌套里时,函数里面嵌套的函数是属于哪个object的,this就是哪个object,如果没有就是window。

摒弃语法糖,func()转化为func.call(undefined,arg1,arg2)

func(p1, p2) 等价于
func.call(undefined, p1, p2)

obj.child.method(p1, p2) 等价于
obj.child.method.call(obj.child, p1, p2)

箭头函数中

箭头函数只能用赋值式写法,不能用声明式写法

默认绑定外层this,不能用call方法修改里面的this。

举例:

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
function X(){
return object = {
name: 'object',
options: bull,
f1(x){
this.options = x
this.f2()
},
f2(){
this.options.f2.call(this)
}
}
}
var options = {
name: 'options',
f1(){},
f2(){
console.log(this) //this是啥?//结果是object
}
}

var x = X()
x.f1(options)

//由于X()就是object,x.f1(options)相当于object.f1.call(object,options)
//进入到f1(x){}这里,这里的x是参数也就是options,f1(options){}里的this是object
//this.f2()→object.f2();this.options =x 也就是options
//相当于options.f2.call(object)

new和构造函数

以游戏中,大批量创造小兵为例。

1
2
3
4
5
6
7
8
9
10
11
12
13
var 小兵 = {
ID: 1, //区别每个士兵
power: 5, //攻击力
life: 50, //生命值
run: function(){/*奔跑的代码*/}
attack: function(){/*攻击的代码*/}
died: functong(){/*死亡的代码*/}
}
var 小兵2 = { ... }
var 小兵3 = { ... }
兵营.制造(小兵)
兵营.制造(小兵2)
兵营.制造(小兵3)

当需要大批量成千上百制造小兵时,可以用一个for循环。

1
2
3
4
5
6
7
8
9
10
var 小兵们 = []
var 小兵
for(var i=0;i<100;i++){
小兵 = {
ID: i,
/* 其余奔跑,攻击,死亡等代码 */
}
小兵们.push(小兵)
}
兵营.批量制造(小兵们)

但使用For循环时,每一个士兵中的奔跑代码,攻击代码等是一模一样,但是都会占用内存,这样就会使占用内存非常大。

引入原型链改进:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var 小兵共有属性 = {
兵种: '无名小卒',
power: 5, //攻击力
run: function(){/*奔跑的代码*/}
attack: function(){/*攻击的代码*/}
died: functong(){/*死亡的代码*/}
}
var 小兵们= []
var 小兵
for(var i=0;i<100;i++){
小兵 = {
ID: i,
life: 50
}
小兵.__proto__ =小兵共有属性
小兵们.push(小兵)
}
兵营.批量制造(小兵们)

再优化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//制造小兵
function create小兵(id){
var 小兵 = {
ID: i,
life: 50
}
小兵.__proto__ = create小兵.小兵共有属性
return 小兵
}
create小兵.小兵共有属性 = {
兵种: '无名小卒',
power: 5, //攻击力
run: function(){/*奔跑的代码*/}
attack: function(){/*攻击的代码*/}
died: functong(){/*死亡的代码*/}
}

//使用生产小兵,使用与制造分开
var 小兵们 = []
for(var i=0;i<100;i++){
小兵们.push(create小兵(i))
}
兵营.批量制造(小兵们)

new的出现:

new.jpg

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
//制造小兵
function 小兵(id){
//var temp = {} //new的作用省略了4步注释的代码 //1
//this = temp //2
//this.__proto__ = create小兵.prototype //3
//create小兵.prototype = {constructor: 小兵} //5
this.ID = id, //自有属性
this.生命值 = 50 //自有属性
//return this //4
}
小兵.prototype = {
//共有属性
constructor: 小兵, //记录由谁构造(构造函数),直接对prototype赋值会覆盖掉上面第5步内容需要重新
兵种: '无名小卒', //需要重新对constructor赋值
power: 5, //攻击力
run: function(){/*奔跑的代码*/}
attack: function(){/*攻击的代码*/}
died: functong(){/*死亡的代码*/}
}

//使用生产小兵,使用与制造分开
var 小兵们 = []
for(var i=0;i<100;i++){
小兵们.push( new 小兵(i))
}
兵营.批量制造(小兵们)

直接重写prototype是比较危险的做法,建议的用法是将共有属性添加进prototype。

1
2
3
小兵.prototype.兵种 = '无名小卒'
小兵.prototype.power = 5
//依次类推

new小结

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var object = new Object()
//自有属性空
//object.__proto__ === Object.prototype

var array = new Array('a','b','c')
//自有属性0:'a',1:'b',2:'c',length: 3
//array.__proto__ === Array.prototype
//Array.prototype.__protot__ === Object.prototype

var fn = new Function('x','y','renturn x+y')
//自有属性 length:2,不可见函数体:'return x+y'
//fn.__proto__ === Function.prototype

Array is a function
Array = function(){...}
Array.__proto__ === Function.prototype

参考引用:

JavaScript 中的 this

this 的值到底是什么?一次说清楚

JS 的 new 到底是干什么的?

初识MVC

发表于 2019-03-29

前端的MVC是什么

MVC 架构模式是指 Model-View-Controller(数据层-视图层-控制器层)模式。Model - 封装数据操作,View - 视图渲染,Controller - 控制器主要负责逻辑以及其他的。

在MVC的基础上,还衍生出了MVP模式,MVVM模式。例如MVP模式(如下图):

MVP.jpg

MVP 模式是MVC基础上,将 Controller 改名为 Presenter。MVP各部分之间的通信,都是双向的,View与Model之间不直接通信。

MVC的尝试使用

代码模块化

​ 代码模块化是指,将一堆功能的代码,按照各自功能抽离到独立js文件,使用立即执行函数包裹所有代码,不暴露全局变量。

以创建一个留言区为例

留言.jpg

HTML:

1
2
3
4
5
6
7
8
9
10
11
12
<section  class="message" >
<h2>留言</h2>
<ol id="messageList">
</ol>
<form id="postMessageForm" >
<label for="">姓名:</label>
<input type="text" name="name">
<label for="">内容:</label>
<input type="text" name="content">
<input type="submit" value="提交">
</form>
</section>

JS:

1.0版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//初始化LeanCloud数据库
var APP_ID = '*******************'; //每个LeanCloud应用的ID和KEY都是私有独立的
var APP_KEY = '************************'; //

AV.init({
appId: APP_ID,
appKey: APP_KEY
});
//上传留言
let myForm = document.querySelector('#postMessageForm')
myForm.addEventListener('submit',function (e) {
e.preventDefault()
let content = myForm.querySelector('input[name=content]').value
var Message = AV.Object.extend('Message')
var message = new Message()
message.save({
'content' : content
}).then(function (object) {
alert('留言成功')
})
})

在这里,采用LeanCloud作为我们的一个后台数据库储存数据。LeanCloud有开发版免费使用,在请求订阅低的

时候,个人demo或者小项目都可以采用。

1.01版本:可以从数据库读取留言并展示出来

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
var query = new AV.Query('Message')
query find() //展示数据库存储的留言
.then(
function (messages) {
let array = messages.map((item)=> item.attributes)
array.forEach( (item)=> {
let li = document.createElement('li')
li.innerText= item.content
let messageList = document.querySelector('#messageList')
messageList.appendChild('li')
})
}
)
let myForm = document.querySelector('#postMessageForm') //点击留言功能
myForm.addEventListener('submit',function (e) {
e.preventDefault()
let content = myForm.querySelector('input[name=content]').value
var Message = AV.Object.extend('Message')
var message = new Message()
message.save({
'content' : content
}).then(function (object) {
alert('留言成功')
})
})

1.02版本:即时免刷新显示用户留言

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
var query = new AV.Query('Message');
query.find() //展示数据库存储的留言
.then(
function (messages) {
let array = messages.map((item)=> item.attributes).reverse()
array.forEach( (item) => {
let li = document.createElement('li')
li.innerText= `${item.name}: ${item.content}`
let messageList = document.querySelector('#messageList')
messageList.appendChild(li)
})
}
)
let myForm = document.querySelector('#postMessageForm') //点击留言功能
myForm.addEventListener('submit',function (e) {
e.preventDefault()
let content = myForm.querySelector('input[name=content]').value
let name = myForm.querySelector('input[name=name]').value
var Message = AV.Object.extend('Message')
var message = new Message()
message.save({
'name': name,
'content' : content
}).then(function (object) { //假刷新功能,即时免刷新将用户留言展现出来
let li = document.createElement('li')
li.innerText= `${object.attributes.name}: ${object.attributes.content}`
let messageList = document.querySelector('#messageList')
messageList.prepend(li)
myForm.querySelector('input[name=content]').value = '' //点击提交后清空留言内容
})
})

1.10版本:用MVC模式封装代码

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
!function () {
//MVC的M
var model = {
//获取数据
init: function () {
var APP_ID = '4sAEIQMOAHFjjEs525eqbRHC-9Nh9j0Va';
var APP_KEY = 'mAiD9Kb9q74Uo9EWA8gi1fv9';

AV.init({
appId: APP_ID,
appKey: APP_KEY
})
},
fetch: function () {
var query = new AV.Query('Message');
return query.find()
},
//创建数据
save: function (name, content) {
var Message = AV.Object.extend('Message')
var message = new Message();
return message.save({
'name': name,
'content': content
})
}
}
//MVC的V
var view = document.querySelector('section.message')
//MVC的C
var controller = {
view: null,
model: null,
messageList: null,
init: function (view, model) {
this.view = view
this.model = model
this.messageList = view.querySelector('#messageList')
this.form = view.querySelector('form')
this.model.init()
this.loadMessages()
this.bindEvents()
},

loadMessages: function () {
this.model.fetch().then(
(messages) => {
let array = messages.map((item) => item.attributes).reverse()
array.forEach((item) => {
let li = document.createElement('li')
li.innerText = `${item.name}: ${item.content}`
this.messageList.appendChild(li)
})
}
)
},
bindEvents: function () {
this.form.addEventListener('submit', (e)=> {
e.preventDefault()
this.saveMessage()
})
},
saveMessage: function () {
let myForm = this.form
let content = myForm.querySelector('input[name=content]').value
let name = myForm.querySelector('input[name=name]').value
this.model.save(name, content).then(function (object) {
let li = document.createElement('li')
li.innerText = `${object.attributes.name}: ${object.attributes.content}`
let messageList = document.querySelector('#messageList')
messageList.prepend(li)
myForm.querySelector('input[name=content]').value = ''
})
}
}

controller.init(view, model)
}.call()

MVC里的套路,在MVC里有一些固定的模板格式,基本遵循下图所示:

MVC格式套路.jpg

面向对象编程(OOP)

当面对大型项目时,一堆的MVC,可以把MVC里面的model,view,controller各自提取出一个模板(函数)封装起来,把需要修改的地方作为参数传进去。

wiki解释:

面向对象程序设计(英语:Object-oriented programming,缩写:OOP)是种具有对象)概念的程序编程典范,同时也是一种程序开发的抽象方针。它可能包含数据、属性&action=edit&redlink=1)、代码与方法)。对象则指的是类)的实例。它将对象)作为程序的基本单元,将程序和数据封装)其中,以提高软件的重用性、灵活性和扩展性,对象里的程序可以访问及经常修改对象相关连的数据。在面向对象程序编程里,计算机程序会被设计成彼此相关的对象。

MDN上给了一堆术语:

  • Namespace 命名空间

    允许开发人员在一个独特,应用相关的名字的名称下捆绑所有功能的容器。

  • Class 类

    定义对象的特征。它是对象的属性和方法的模板定义。

  • Object 对象

    类的一个实例。

  • Property 属性

    对象的特征,比如颜色。

  • Method 方法

    对象的能力,比如行走。

  • Constructor 构造函数

    对象初始化的瞬间,被调用的方法。通常它的名字与包含它的类一致。

  • Inheritance 继承

    一个类可以继承另一个类的特征。

  • Encapsulation 封装

    一种把数据和相关的方法绑定在一起使用的方法。

  • Abstraction 抽象

    结合复杂的继承,方法,属性的对象能够模拟现实的模型。

  • Polymorphism 多态

    多意为「许多」,态意为「形态」。不同类可以定义相同的方法或属性。

我们在写代码时,当完成了一个需求后,我们就需要对我们的代码中不断地进行解耦、抽象抽离、封装接口,将重复的代码、功能性的代码进行封装,“为变化服务”!

参考文章:

MVC,MVP 和 MVVM 的图示

每日一题」MVC 是什么?

学习AJAX

发表于 2019-03-27

AJAX是什么

AJAX(Asynchronous JavaScript and XML):异步的JavaScript和XML。用 JS 发起一个请求,并得到服务器返回的内容。异步请求数据,局部更新页面内容。

window.XMLHttpRequest()

  1. 使用XMLHttpRequest发送请求
  2. 服务器返回XML格式的字符串(XML后来用JSON替代)
  3. JS解析XML,并局部更新页面

一个简易的AJAX发请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
myButton.addEventListener('click',(e) => {
let request = new XMLHttpRequest()
request.open('GET','./xxx') //配置request
request.send()
request.onreadystatechange = ()=>{
if(request.readyState === 4){
console.log('请求响应都完毕了')
if(request.status >= 200 && request.status <= 300){
console.log ('说明请求成功')
let string =request.responseText
//把符合的JSON语法的字符串 转换成JS对应的值
let object = window.JSON.parse(string)

}else if (request.status >= 400) {
console.log('请求失败')
}
}
}
})

同源策略和CORS跨域

同源策略.png

同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。目的是为了保证用户信息的安全,防止恶意的网站窃取数据。

所谓同源是指”协议+域名+端口”三者相同。

协议相同
域名相同
端口相同

同源策略限制内容有:

  • Cookie、LocalStorage、IndexedDB 等存储性内容
  • DOM 无法获得
  • AJAX 请求发送后,结果被浏览器拦截(request.status = 0)

    突破同源策略:

  • JSONP

  • WebSocket
  • CORS
  • 架设服务器代理

CORS跨域:(Cross-Origin Resource Sharing)服务端设置response.setHeader('Access-Control-Allow-Origin','http://(允许访问的域名)')就可以开启 CORS。 该属性表示哪些域名可以访问资源,如果设置通配符则表示所有网站都可以访问资源。

form表单提交可以跨域是因为原页面用form提交到另外一个域名后,会自动刷新跳转另外一个域名,原来页面的脚本无法获得新页面的内容。

自己封装一个window.jQuery.ajax

先自己创建个jQuery

1
2
3
4
5
6
7
window.jQuery = function (nodeOrSelector) {
let nodes = { }
nodes.addClass = function(){}
nodes.html = function () {}
return nodes
}
window.$ = window.jQuery
  1. 第一版
1
2
3
4
5
6
7
8
9
10
11
12
13
14
window.jQuery.ajax = function (url, method, body, successFn, failFn) {
let request = new XMLHttpRequest()
request.open(method,url) //配置request 请求第一部分
request.onreadystatechange = ()=>{
if(request.readyState === 4){
if(request.status >= 200 && request.status <= 300){
successFn.call(undefined,request.responseText)
}else if (request.status >= 400) {
failFn.call(undefined,request)
}
}
}
request.send(body)
}
1
2
3
4
5
6
7
8
9
myButton.addEventListener('click',(e) => {
window.jQuery.ajax(
'./xxx',
'post',
'a=1&b=2',
(responseText) => {console.log(1)},
(request) => {console.log(2)}
)
})
  1. 优化传参
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
window.jQuery.ajax = function ({url, method, body, headers,successFn,failFn}) {
request.open(method,url) //配置request 请求第一部分
for(let key in headers){ //遍历设置多个header
let value = headers[key]
request.setRequestHeader(key, value)
}
request.onreadystatechange = ()=>{
if(request.readyState === 4){
if(request.status >= 200 && request.status <= 300){
successFn.call(undefined,request.responseText)
}else if (request.status >= 400) {
failFn.call(undefined,request)
}
}
}
request.send(body)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
myButton.addEventListener('click',(e) => {
window.jQuery.ajax({
url: './xx',
method: 'get',
headers: {
'content-type': 'application/x-www-form-urlencoded',
'tom': '18'
},
successFn: (x)=>{
f1.call(undefined,x) //成功后执行两个函数function f1(responseText){};
f2.call(undefined,x) // function f2(responseText){}
},
failFn: (x)=>{
console.log(x)
console.log(x.status)
console.log(x.responseText )
}
})
})
  1. 添加Promise功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
window.jQuery.ajax = function ({url, method, body, headers}) {
return new Promise(function (resolve, reject) {
let request = new XMLHttpRequest()
request.open(method, url) //配置request 请求第一部分
for (let key in headers) { //遍历设置多个header
let value = headers[key]
request.setRequestHeader(key, value)
}
request.onreadystatechange = () => {
if (request.readyState === 4) {
if (request.status >= 200 && request.status <= 300) {
resolve.call(undefined, request.responseText)

} else if (request.status >= 400) {
reject.call(undefined, request)
}
}
}
request.send(body)
})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
myButton.addEventListener('click',(e) => {
window.jQuery.ajax({
url: './xxx',
method: 'get',
headers: {
'content-type': 'application/x-www-form-urlencoded',
'tom': '18'
}
}).then(
(text) => {console.log(text)},
(request) => {console.log(request)}
)
// let promise = .... //promise.then()
})

学习JSONP

发表于 2019-03-26

JSONP是什么

请求方:tom.com的前端程序员(浏览器)

相应方:jerry.com的后端程序员(服务器)

  1. 请求方动态创建script,src指向响应方,同时传一个查询参数?callbackName=yyy
  2. 响应方根据查询参数callbackName,构形如yyy.call(undefined,'你要的数据')这样的响应
  3. 请求方接收到响应后,执行yyy.call,如此就得到需要的数据
JSONP为什么不能使用post

答:因为JSONP是通过动态创建script来实现的,动态创建script只能通过GET请求方法,不能使用POST请求方法。

以点击按钮扣一块为例:

1
2
3
<h5>您的账户余额是 <span id="amount">&&&amount&&&</span></h5>
<!-- &&&amount&&& 是占位符-->
<button id="button">付款</button>

JSONP出现前的历史做法:(拥有src属性的标签都可以跨域发送请求)

  1. 使用form表单提交POST请求,但是一定会刷新当前页面或者一个iframe页面。

    1
    2
    <form ... target="result">
    <iframe name="result">
  2. 局部刷新,不用form和frame时,使用script创建img发请求(1x1px空白像素图片),通过服务器返回的状态码来判断请求成功或失败。缺陷只能用GET,不能用POST。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 图片发请求,js代码
    let image =document.createElement('img')
    image.src = '/pay'
    image.onload = function () {
    alert("成功")
    amount.innerText = amount.innerText - 1
    }
    image.onerror = function () {
    alert("失败")
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // 路由代码
    else if (path === '/pay' ){
    var amount = fs.readFileSync('./db','utf8') // 读数据 100
    var newAmount = amount - 1
    fs.writeFileSync('./db',newAmount)
    response.setHeader('Content-Type','image/png')
    response.statusCode = 200
    response.write(fs.readFileSync('./dog.png'))//返回一个图片
    response.end()
    } else{
    response.statusCode = 404
    response.setHeader('Content-Type', 'text/html;charset=utf-8')
    response.write('找不到对应路径,请修改')
    response.end()
    }
  1. script发请求,然后appendChild到body上,响应结束后把script删除。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //js代码
    button.addEventListener('click', (e)=>{
    let script = document.createElement('script')
    script.src = '/pay'
    document.body.appendChild(script)
    script.onload = function(e){
    e.currentTarget.remove()
    }

    script.onerror = function () {
    alert('fail')
    e.currentTarget.remove()
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //路由代码
    else if (path === '/pay' ){
    var amount = fs.readFileSync('./db','utf8') // 100
    var newAmount = amount - 1
    fs.writeFileSync('./db',newAmount)
    response.setHeader('Content-Type','application/javascript')
    response.statusCode = 200
    response.write(`
    amount.innerText = amount.innerText - 1`)
    response.end()
    } else{

写出一个JSONP

假设tom.com向jerry.com发送请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//tom.com
button.addEventListener('click', (e)=>{
let script = document.createElement('script')
let functionName = 'tom' + parseInt(Math.random() * 10000, 10)
window[functionName] = function (result) {
if (result === 'success') {
amount.innerText = amount.innerText - 1
} else {
}
}
script.src = 'http://jerry.com:8002/pay?callback=' + functionName
document.body.appendChild(script)
script.onload = function (e) {
e.currentTarget.remove()
delete window[functionName]
}
script.onerror = function () {
alert('fail')
e.currentTarget.remove()
delete window[functionName]
}
1
2
3
4
5
6
7
8
9
10
11
12
//jerry.com的路由
else if (path === '/pay' ){
var amount = fs.readFileSync('./db','utf8') // 100
var newAmount = amount - 1
fs.writeFileSync('./db',newAmount)
response.setHeader('Content-Type','application/javascript')
response.statusCode = 200
response.write(`
${query.callback}.call(undefined,'success')
`)
//${query.callbackName}获取传参 把success用json表示时就是JSONP
response.end()

jQuery已经封装好了相关函数,写法如下:

1
2
3
4
5
6
7
8
9
10
11
button.addEventListener('click', (e)=>{
//jQuery写jsonp
$.ajax({
url: 'http://jerry.com:8002/pay',
dataType: 'jsonp',
success: function (response) {
if(response === 'success'){
//...code...//
}
}
})

学习jQuery

发表于 2019-03-25

jQuery的基本设计思想和主要用法,就是“选择某个网页元素,然后对其进行某种操作”。

jQuery选择器

1
2
3
4
$('#id');$('.class');$('span')
$('div > p') //<div> 元素的直接子元素的所有 <p> 元素
$('div p') //<div> 元素的后代的所有 <p> 元素
$('span.red'); // 找出<span class="red ...">...</span>
1
2
3
4
5
6
7
8
9
10
<ul>
<li>1</li>
<li>2</li>
</ul>

// $('li') 是一个伪数组的jQuery对象,{
0: li,
1: li,
legth: 2}
它的原型(共享属性)为 jQuery.prototype
1
2
3
4
5
6
7
8
9
10
11
12
<div id='xxx'></div>

var div = document.getElementById('xxx')
var $div =$('#xxx')

//$(div) 可以将 div 封装成一个 jQuery 对象,就跟 $div 一样
//$div[0] === div ,$div 的第一项就是 div

//div 和 $div 的区别是:
//div为DOM对象,属性和方法有 childNodes firstChid nodeType 等
//$div为jQuery对象,属性和方法有 addClass removeClass toggleClass 等
//分清 DOM 和 jQuery 对象,他们都只拥有自己对象的方法

jQuery常用API

.get():通过jQuery对象获取一个对应的DOM元素,返回的是一个DOM对象

.eq():返回为指定的索引的哪一个元素,返回的是一个jQuery对象

addClass();removeClass()

.toggleClass()//如果在元素中指定类名称不存在,则添加指定类名称;如果元素已经拥有指定
类名称,则从元素中删除指定类名称。

1
2
3
4
5
6
7
8
9
10
11
<div></div><div></div>

$("<p></p>/>")
.appendTo("div") //将匹配的元素插入到目标元素内部的最后面
.addClass("test")
.end()
.addClass("test2");

//结果:
<div><p class="test test2"></p></div>
<div><p class="test"></p></div>

自制一个jQuery

jQuery实质上是一个构造函数,该构造函数接受一个参数,jQuery通过这个参数利用原生API找到节点,之后返回一个方法对象,该方法对象上的方法对节点进行操作(方法使用了闭包)。

通过自制拥有几个简易功能的jQuery,来加深理解jQuery。

要求:

1
2
3
4
5
6
7
>window.jQuery = ???
>window.$ = jQuery
>
>var $div = $('div')
>$div.addClass('red') // 可将所有 div 的 class 添加一个 red
>$div.setText('hi') // 可将所有 div 的 textContent 变为 hi
>

ⅰ. 获取元素节点,并返回一个纯净的伪数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
}
}

return node
}

ⅱ. 添加addClass()方法

1
2
3
4
nodes.addClass = function (className) {
for (let i = 0; i < nodes.length; i++) {
nodes[i].classList.add(className)
}

ⅲ. 添加setText()方法:

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

组合一起就是:

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
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
}
}

nodes.addClass = function (className) {
for (let i = 0; i < nodes.length; i++) {
nodes[i].classList.add(className)
}

nodes.setText = function (text) {
if(text === undefined){
var texts = []
for(i =0;i<nodes.length; i++){
texts.push(nodes[i].textContent)
}
return texts
}else {
for (let i =0;i<nodes.length;i++){
nodes[i].textContent = text
}
}
}
return nodes
}

这里是实现的思路:

第一版:

1
2
3
4
5
6
7
8
function addClass(node1, className){
var node = node1
node.classList.add(className)
}
function setText(node1, value){
var node = node1
node.innerText = value
}

第二版:

1
2
3
4
5
6
7
8
9
10
11
12
13
window.jQuery = function(node1){
var node = node1

node.addClass = function(className){
node.classList.add(className)
}

node.setText = function(value){
node.innerText = value
}

return node
}

第三版: 支持输入字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
window.jQuery = function(node1){
var node

if(typeof node1 === "string"){
node = document.querySelector(node1)
}else{
node = node1
}

node.addClass = function(className){
node.classList.add(className)
}

node.setText = function(value){
node.innerText = value
}

return node
}

第四版: 支持操作多个元素

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
window.jQuery = function(node1){
var node

if(typeof node1 === "string"){
node = document.querySelectorAll(node1)
}else{
node = {}
node[0] = node
node["length"] = 1
}

node.addClass = function(className){
for(var i = 0; i < node.length; i++){
node[i].classList.add(className)
}
}

node.setText = function(value){
for(var i = 0; i < node.length; i++){
node[i].innerText = value
}
}

return node
}

第五版: 通过jQuery返回的是纯净的伪数组

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
window.jQuery = function(node1){
var node = {}

if(typeof node1 === "string"){
var temp = document.querySelectorAll(node1)
for(var j = 0; j < temp.length; j++){
node[j] = temp[j]
}
node["length"] = j
}else{
node = {}
node[0] = node
node["length"] = 1
}

node.addClass = function(className){
for(var i = 0; i < node.length; i++){
node[i].classList.add(className)
}
}

node.setText = function(value){
for(var i = 0; i < node.length; i++){
node[i].innerText = value
}
}

return node
}

DOM学习

发表于 2019-03-05

DOM树

DOM(Document Object Model):文档对象模型。

由于HTML文档被浏览器解析后就是一棵DOM树,要改变HTML的结构,就需要通过JavaScript来操作DOM。

DOM树.png

上图就是DOM树,DOM 的最小组成单位叫做节点(node)其中每个节点的都是Node类型:

  • Document:整个文档树的顶层节点

  • DocumentType:doctype标签(比如<!DOCTYPE html>)

  • Element:网页的各种HTML标签(比如<body>、<a>等)

  • Attribute:网页元素的属性(比如class="right")

  • Text:标签之间或标签包含的文本

  • Comment:注释

  • DocumentFragment:文档的片段

所有DOM节点对象都继承了Node接口,这是 DOM 操作的基础。

Node接口的属性和方法

对于DOM的操作,最主要归纳于四个字:增、删、改、查。

首先是查:

Document.getElementsByClassName();//返回指定Class name的节点

Document.getElementById();//返回指定ID的节点

Document.querySelector();//接受一个 CSS 选择器作为参数,返回第一个匹配该选择器的元素节点

Document.querySelectorAll();//返回一个NodeList对象,包含所有匹配给定选择器的节点。

//两种方法获取的节点是有有区别的,document.querySelectorAll方法返回的是一个静态集合。DOM内部的变化,并不会实时反映在该方法的返回结果之中。

getElementBy与querySelector区别.png

在得到相应Node节点时候, Node节点本身也有一些重要属性:

nodeName//返回节点类型,重要的返回的值有大写的HTML元素名, #text,#document

nodeType//属性返回一个整数值,表示节点的类型。文档节点(document):9,元素节点(element):1;文本节点(text):3

nodeValue//返回一个字符串,表示当前节点本身的文本值,只有文本节点(text)、注释节点(comment)和属性节点(attr)有返回结果,其他节点都返回null

textContent//属性自动忽略当前节点内部的 HTML 标签,返回所有文本内容。//而innerText在IE下会忽略隐藏(例如css,style),同时也会重置写入。

兄弟关系:Node.nextSibling;Node.previousSibling//会获取到文本节点(例如回车)

父子关系:node.parentNode;node.childNodes;node.firstChild;node.lastChild

其中node.childNodes属性返回一个伪数组的对象(NodeList集合);伪数组内的值是动态变化的,回车也会充当文本体现出来。

Node方法:

  • Node.appendChild()//将其作为最后一个子节点,插入当前节点
  • Node.hasChildNodes()//返回一个布尔值,表示当前节点是否有子节点
  • Node.cloneNode()//克隆一个节点,接受参数true为表示同时克隆子节点
  • Node.insertBefore(newNode, referenceNode)//将某个节点插入父节点内部的指定位置。
  • Node.removeChild()用于从当前节点移除该子节点,被移除的节点依然存在于内存之中,但不再是 DOM 的一部分。
  • Node.replaceChild(newChild, oldChild)//替换当前节点的某一个子节点
  • Node.contains()
  • Node.isEqualNode()//用于检查两个节点是否相等,指的是两个节点的类型相同、属性相同、子节点相同。
  • Node.isSameNode()//表示两个节点是否为同一个节点。
  • Node.normalize()

Document节点的属性和方法:

Document节点属性有:

document.hidden返回一个布尔值,表示当前页面是否可见。配合document.visibilityState(返回文档的可见状态)使用。

document.referrer表示当前文档的访问者来自哪里,可用于网站图片防盗链,流量,DDOS。

  • document.characterSet;document.title;document.domain;document.styleSheets;document.scripts;document.plugins
  • document.images;document.links;document.forms;
  • document.doctype;document.documentElement//返回HTML;
  • document.body;document.head;document.scrollingElement;document.fullscreenElement

Document节点方法有:

DOM的增:

Document.createElement() // 创建元素

Document.createTextNode() //创建一个新的文本节点

Node.appendChild()//插入当前节点

Element.classList.add() //添加指定的类值class

Document.write();Document.writeIn() // 将文本字符串写入打开的文档流;document.write()和writeIn()的区别是前者没有换行,而后者有换行。但要防止异步、setTimeOut导致冲洗掉整个文档。

DOM的删:

Element.removeAttribute() // 从元素中删除指定的属性

Element.removeChild()// 删除子元素

ChildNode.remove() // 删除元素

Child.parentNode.removeChild(child) // 不确定父元素时可这样删除子元素

Element.classList.remove() // 移除元素中一个或多个类名

DOM的改:

Node.replaceChild() // 替换子节点

style.property = new style // 修改元素CSS属性值

Element.setAttribute() // 设置或者改变指定属性并指定值

Node.innerText // 修改元素文本内容

Element.innerHTML // 设置或获取描述元素后代的HTML语句

DOM事件模型

在2000年11月,DOM标准在Level 2中加入了Events事件模型,后期的Level 3也并未对Events进行修改。

以事件监听为例:

1
2
3
4
5
6
7
8
9
10
11
12
<button id="xxx">点击</button>

xxx.addEventListener('chick',function(){
alert('我被点击了')})
//.addEventListener()方法,可以增加多次事件监听,以队列形式触发,先进先出。

xxx.onclick = function(){
alert('我被点击了')
}
//.onclick方法:属性唯一,只能有一个,多个会覆盖前面。

.one()//只执行一次,然后把自己从事件队列移除。

DOM事件流包括三个阶段。

  1. 事件捕获阶段

  2. 处于目标阶段

  3. 事件冒泡阶段

    DOM事件流.png

    xxx.addEventListener('chick',function(){},false): 事件监听默认为false,冒泡阶段触发。当值为true时,捕获阶段触发;处于捕获阶段的事件会比冒泡阶段先发生。

    DOM事件流1.png

DOM事件流2.png

实现一个点击弹出popover

要求

  1. 点击按钮弹出浮层
  2. 点击别处关闭浮层
  3. 点击浮层时,浮层不得关闭
  4. 再次点击按钮,浮层消失

点击popover.png

参考文献:

Javascript DOM 元素的增删改查

对 DOM 的一些理解

廖雪峰-操作DOM

阮一峰教程-DOM

​

JS基础:Function

发表于 2019-03-03

函数的声明方法

1、具名函数:

1
2
function x(输入1,输入2){
return undefined}

2、匿名函数:(需要给一个变量)

1
2
var x
x = function(){}

3、把具名函数赋值给变量:

1
2
3
var x = function y(){
console.log(y) //会报错: y is not defined
}

4、使用window.Function函数对象

1
2
3
4
// new Function('x','y','return x + y')
var n = 1
f = new Function('x','y','return x +' +n+ '+y')
f(1,2) //4

5、箭头函数(也属于匿名函数)

1
2
3
// (x,y) => {return x + y}
(x,y) => x + y //只能只有一句语句时适合
n => n*n //只有一个参数时适合

6、函数的name属性

1
2
3
4
5
6
7
8
9
10
11
function f1(){}
f1.name // "f1"

var f2 = function(){}
f2.name //"f2"

var f3 = function f4(){}
f3.name // "f4"

f5 = new Function()
f5.name //"anonymous"(匿名)

函数的this和arguments

函数本质是:调用call;用于反复调用的代码块。

f:函数对象;f.call():执行这个函数体。

1
2
f.call(asThis,input1,input2)
//其中asThis 会被当做this,[input1,input2]会被当做arguments

其中f.call的第一个参数可以用this得到;f.call的第二个以及后面的参数可以用arguments得到,并以伪数组形式呈现。

1
2
3
4
f.call(undefined,'x','y','z'){
console.log(arguments) //['x,'y','z']以伪数组形式呈现
console.log(this) //在普通模式下,this是undefined,浏览器会指向window
} //在'use strict'严格模式下,this就是undefined

函数的调用栈(Call Stack)

JavaScript的函数调用栈(Call Stack)其实就是一种解析器去处理程序的机制,它是栈数据结构。它能追踪子程序的运行状态。当函数调用的时候,就把该函数push到调用栈,结束时候,就从栈顶端移除。遵循FILO(先进后出)原则。另外当递归压栈时,容易造成stack overflow。

这个网站 可以可视化帮助理解调用栈。

函数的作用域(Function Scope)

作用域(scope)指的是变量存在的范围。在 ES5 的规范中,JavaScript 只有两种作用域:一种是全局作用域,变量在整个程序中一直存在,所有地方都可以读取;另一种是函数作用域,变量只在函数内部存在。ES6 又新增了块级作用域。

JavaScript 语言特有的”链式作用域”结构(chain scope),子对象会就近原则,一级一级地向上寻找最近父对象的变量。与全局作用域一样,函数作用域内部也会产生“变量提升”现象。

立即执行函数(IIFE):是一个在定义时就会立即执行的JavaScript函数。当函数变成立即执行的函数表达式时,表达式中的变量不能从外部访问。

1
2
3
4
5
6
//使用方法
( function(){/* code */}() );
!function () { /* code */ }();
~function () { /* code */ }();
-function () { /* code */ }();
+function () { /* code */ }();

闭包:

1
2
3
4
5
6
!function(){
var local = 'xxx'
function foo(){
console.log(local)
}
}();
1
2
3
4
5
6
7
8
9
10
11
function foo(){
var local = 1
function bar(){
local++
return local
}
return bar
}

var func = foo() //func现在是一个闭包
func()

以上两组代码都形成了一个闭包:「函数」和「函数内部能访问到的变量」(也叫环境)的总和,就是一个闭包。

闭包的用处有,一个是可以读取函数内部的变量,一个就是让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在,不会在调用结束后,被垃圾回收机制回收。另一个用处,是封装对象的私有属性和私有方法。

由于外层函数每次运行,都会生成一个新的闭包,而这个闭包又会保留外层函数的内部变量,所以内存消耗很大。因此不能滥用闭包,否则会造成网页的性能问题。

参考引用:

阮一峰的JavaScript

「每日一题」JS 中的闭包是什么?

JS基础:Array

发表于 2019-03-01

数组是最简单的内存数据结构。JavaScript里也有数组类型,用于存储一系列同一种数据类型的值。

数组与伪数组

伪数组(arrLike),是一个类似数组的对象。例如

1
2
3
4
5
6
obj = {
0:a,
1:b,
2:c,
length:3
}

function的arguments对象,还有getElementsByTagName、ele.childNodes等返回的NodeList对象,或者自定义的某些对象,这些都可以是伪数组。
数组跟数组一样按索引方式存储,具有length属性。但是伪数组与数组的最重要区别在于,伪数组的原型链中不包括Array.prototype。

伪数组转换成数组的方法有:

  • 遍历伪数组并加入一个空数组

    1
    2
    3
    4
    var arr =[]
    for(var i =0;i< arrLike.length; i++){
    arr.push(arrLike[i])
    }
  • 数组的slice()方法

    Array.prototype.slice.call(arrLike)

    1
    2
    3
    4
    5
    function list() {
    return Array.prototype.slice.call(arguments);
    }

    var list1 = list(1, 2, 3); // [1, 2, 3]
  • ES6中的Array.from()方法

    1
    2
    3
    4
    5
    6
    Array.from({  
    0:"kk",
    1:18,
    2:"文字文字",
    length:3 });
    //["kk", 18,"文字文字"]

数组属性

数组属性有Array.length;get Array[];Array.prototype

数组方法

数组的操作方法比较多,详细可以查阅MDN。这里简要介绍几种常用的操作方法

举例var num = [1,2,3,4,5]

增删查的方法

查:可以通过索引访问数组元素,如num[1] //2;

增:push方法添加一个或者多个元素到数组末尾,如num.push[7,8];

​ unshift方法将数值直接插入数组的首位,如num.unshift[-2,-1] 。

删: 删除数组最靠后的元素pop();删除数组首位元素shift();

另外,splice()方法可以实现在任意位置添加或删除元素,例如

1
2
3
4
5
6
7
num.splice(2,2) 
//第一个参数表示要删除元素的索引值,第二个参数表示删除元素的个数
//num由[1,2,3,4,5]变成[1,2,5]

num.splice(2,0,7,8,9)
//添加元素,所以第二个参数为了0;第三个元素以后,表示要添加到数组里的值
//num由[1,2,3,4,5]变成[1,2,7,8,9,3,4,5]

Array.prototype.forEach

forEach()方法对数组的每个元素执行一次提供的函数,用法array.forEach(function(value,key){})。需注意的是forEach()不会在迭代之前创建数组的副本,已删除的项不会被遍历到。如果已访问的元素在迭代时被删除了(例如使用 shift()),之后的元素将被跳过。

1
2
3
4
5
6
7
8
9
10
function forEach(array,x){
for (let i =0;i<array.length;i++){
x(array[i],i)
}}
forEach(['a','b','c'],function(value,key){
console.log(value,key)})

//a 0
//b 1
//c 2

Array.prototype.sort()

sort()方法用原地算法对数组的元素进行排序,并返回数组。

array.sort(function(x,y){return x - y})

array.sort(function(x,y){return y -x})

1
2
3
var a = [5,6,3,1,4,2]
var b =a.sort()//b [1,2,3,4,5,6]
//a仍为[5,6,3,1,4,2]

.join() .concat() .map() .filter() .reduce()

join()方法将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串。

1
2
3
a = [1,2,'a']
a.join('cc') // '1cc2cca'
a.join() //'1,2,a'

concat()方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。

1
2
3
a = [1,2,3];b=[4,5,6]
var c = a.concat(b) // [1,2,3,4,5,6]
var d = a.concat([]) // d为新数组[1,2,3]与a不同

map()方法用于遍历数组,操作并收集返回新的数组

1
2
a = [1,2,3]
a.map(value => value*2) //[2,4,6],数组a不变

filter()方法创建一个新数组, 其包含通过所提供函数实现的规则的所有元素。

1
2
3
4
a = [1,2,3,4,5,6,7,8,9]
a.filter(function(value,key){
return value >= 5
}) //[5,6,7,8,9]

reduce()方法对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。

1
2
3
a.reduce(functiong(sum,n){
return sum + n
},0)
1
2
3
4
5
6
7
var a =[1,2,3]
a.reduce(function(arr,n){
arr.push(n * 2)
return arr
},[])
// [2,4,6]
//map()用redcue()表示
1
2
3
4
5
6
7
8
//计算所有奇数和
var a = [1,2,3,4,5,6,7,8,9]
a.reduce(function(arr,n){
if(n % 2 === 1){
return arr + n}
return arr
},0)
//25

参考文献

MDN—JavaScript的标准库Array

简述JS的原型与原型链

发表于 2018-12-20

什么是原型

ECMAScript中规定全局对象叫做 global,但是浏览器把 window 作为全局对象(浏览器先存在的)

window 就是一个哈希表,有很多属性。window 的属性就是全局变量。

这些全局变量分为两种:

  1. 一种是 ECMAScript 规定的
    • global.parseInt
    • global.parseFloat
    • global.Number
    • global.String
    • global.Boolean
    • global.Object
  2. 一种是浏览器自己加的属性
    • window.alert
    • window.prompt
    • window.comfirm
    • window.console.log
    • window.console.dir
    • window.document
    • window.document.createElement
    • window.document.getElementById

由于JavaScript的对象创建不存在拷贝,对象的原型实际上也是一个对象,它和对象本身是完全独立的两个对象。原型是为了共享多个对象之间的一些共有特性(属性或方法),JavaScript中的对象,都有一个内置属性[Prototype],指向这个对象的原型对象。

每个创建对象除了自身属性hasOwnProperty外,都会有__proto__指向公用key属性。

什么是原型链

1
2
var 对象 = new 函数()
对象.__proto__ === 对象的构造函数.prototype

在JavaScript 中,每个对象都有一个指向它的原型(prototype)对象的内部链接。这个原型对象又有自己的原型,直到某个对象的原型为 null 为止(也就是不再有原型指向),组成这条链的最后一环。这种一级一级的链结构就称为原型链。

例如:

1
2
3
4
var s1 = new String('123')
s1.__proto__ === String.prototype // true
String.prototype.__proto__ === Object.prototype //true
Object.prototype.__proto__ === null //true

WX20181221-034313@2x.png

因为Number,String,Object是Function的实例,所以:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var num = new Number()
num.__proto__ === Number.prototype
Number.__proto__ === Function.prototype //true 因为 Number 是 Function 的实例

var obj = new Object()
obj.__proto__ === Object.prototype
Object.__proto__ === Function.prototype //true 因为 Object 是 Function 的实例

var fn = new Function()
fn.__proto__ === Function.prototype
Function.__proto__ === Function.prototype //true 因为 Function 是 Function 的实例!

Function.prototype.__proto__ === Object.prototype //true
Object.prototype.__proto__ === null //true

参考文献:

「每日一题」什么是 JS 原型链?

白话原型和原型链

浅谈JS原型和原型链

深刻理解JavaScript基于原型的面向对象

再谈javascriptjs原型与原型链及继承相关问题

JS的数据类型转换

发表于 2018-12-19

JavaScript 是一种动态类型语言,变量没有类型限制,可以随时赋予任意值。

1数据类型的转换

1.1Number()

使用Number函数,可以将任何类型的值转化为数值。

1
2
3
4
5
6
7
8
9
Number(123)     // 123
Number('123') //123
Number('123abc') // NaN

Number(true) // 1
Number(false) // 0

Number(undefined) // NaN
Number(null) // 0

使用parseInt()函数,可以将值转化为整数值,使用parseFloat()函数,可以将值转化为浮点数。

1
2
3
4
5
parseInt('12.3')  //12
parseInt('12a3c') //12
parseInt(1101,2) //13,表示将二进制数字1101转换成十进制

parseFloat('12.3') //12.3

在实际使用中,很多选择这种方式进行转换

1
2
'123' - 0  //123
+'123' //123

1.2Boolean()

Boolean函数的转换规则相对简单,除以下五个值转换结果为false,其他都为true。

undefined

null

-0 或+0

NaN

‘ ‘ (空字符串)

所有对象,包括空对象的转换结果都是true。

1.3String()

String函数可以将任意类型的值转化成字符串,转换规则如下。

数值:转为相应的字符串。
字符串:转换后还是原来的值。
布尔值:true转为字符串”true”,false转为字符串”false”。
undefined:转为字符串”undefined”。
null:转为字符串”null”。

1
2
String({a: 1}) // "[object Object]"
String([1, 2, 3]) // "1,2,3"

number.toString()函数可以使用不同进制把一个数字转换为字符串。

1
2
3
4
15.toString() //15,默认将数字15转为十进制后输出字符串
15.toString(2) //1111,将数字15转为二进制后输出字符串
15.toString(8) //17
15.toString(16) //f

在实际使用中,可以+''来时实现转换。

1
2
3
4
123 + '' //'123'
true + '' // 'true'
obj + '' // '[obj obj]'
123 + '12' // '12312'

2各数据类型的内存存储

内存数据区存储分为Stack栈内存和Heap堆内存。

简单类型的值储存在Stack栈内存里,数字以64位储存,字符串以16位储存。

复杂类型将值存储在Heap堆内存,在Stack栈内存上记录其Heap堆内存地址。

####2.1浅拷贝和深拷贝

对于简单类型的数据来说,赋值就是深拷贝。
对于复杂类型的数据(对象)来说,才要区分浅拷贝和深拷贝。深拷贝是对 Heap 内存进行完全的拷贝。

下面有几道常见面试题,在解答时只需要画出简略内存图就可以明知答案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 一
var a = 1
var b = a
b = 2
a // 1

// 二
var a = {name: 'a'}
var b = a
b = {name: 'b'}
a.name //'a'

// 三
var a = {name: 'a'}
var b = a
b.name = 'b'
a.name // 'b'

// 四
var a = {name: 'a'}
var b = a
b = null
a // {name: 'a'}

参考文献:

阮一峰的JavaScript教程

12

LuoYupeng

19 日志
5 标签
© 2019 LuoYupeng
由 Hexo 强力驱动
|
主题 — NexT.Pisces v5.1.4