0%

1.判断Node类型:

someNode.nodeType == 1 // ELEMENT_NODE
someNode.nodeType == 2 // ATTRIBUTE_NODE
someNode.nodeType == 3 // TEXT_NODE
someNode.nodeType == 9 // DOCUMENT_NODE

2.要了解节点的具体信息可以使用nodeName或nodeValue属性,必须用在ELEMENT_NODE

例:

1
2
3
if(someNode.nodeType === 1) {
value = someNode.nodeName; // nodeName 的值是元素的标签名
}

3.每个节点都有childNodes属性,它实际上是基于DOM结构动态执行查询的结果,类数组。

例:

1
2
var firstChild = someNode.childNodes[0];
var secondChild = someNode.childNodes.item(1);

可以使用Array.prototype.slice转换为数组:
var arrayOfNodes = Array.prototype.slice.call(someNode.ChildNodes,0); // 在IE8及之前IE版本无效,原因是IE早期将NodeList实现为COM对象
解决方案:(兼容性)

1
2
3
4
5
6
7
8
9
10
11
12
function convertToArray(nodes) {
var arr = null;
try {
arr = Array.prototype.slice.call(nodes,0); // 针对非IE浏览器
}catch(e) {
arr = new Array();
for(var i =0,len = nodes.length; i< len; i++) {
arr.push(nodes[i])
}
}
return arr;
}

4.父节点:parantNode属性

第一个子节点:firstChild 最后一个子节点:lastChild 
同胞上一节点:previousSibling 同胞下一节点:nextSibling
所有节点的最后一个属性是ownerDocument,该属性指向表示整个文档的文档节点。

5.操作节点:

1) 插入:

var returnedNode = someNode.appendChild(newNode) // 用于childNodes列表末尾中插入节点,someNode为父节点,返回一个新节点
如果在调用appendChild()时传入父节点的第一个子节点,那么该节点就会成为父节点的最后一个子节点。
insertBefore(要插入的节点,参照节点) // 插入一个新节点在参照节点之前
例:

1
2
someNode.insertBefore(newNode,null); // 插入成为最后一个子节点
someNode.insertBefore(newNode,someNode.firstChild); // 插入成为第一个子节点

appendChild()和insertBefore()只插入节点,并不会移除节点。

2) 替换:

replaceChild(要插入的节点,要替换的节点) // 要替换的节点将由这个方法返回并从文档树被移除,同时由要插入的节点占据其位置
例: var returnedNode = someNode.replaceChild(newNode,someNode.firstChild); // 新节点替换第一个子节点

3) 移除:

removeChild(rmNode); // 接收一个要移除的节点为参数,被移除的节点为返回值
例: var rmNode = someNode.removeChild(someNode.firstChild); // 移除第一个子节点

6.Document类型常见作为HTMLDocument实例的document对象

1) 文档信息:

document.title; // 取得页面标题,赋值也可以修改标题
document.url; // 取得完整的URL
document.domain; // 取得主机域名
document.referrer; // 取得来源页面的URL

2) 查找元素:

document.getElementById(‘id’); // 等等

3) 特殊集合:

document.anchors // 包含文档中所有带name特性的元素
document.forms; // 包含文档中form元素
document.images; // images元素
document.links; // 返回带有href特性的
元素

7.所有HTML元素都由HTMLElement类型表示,可直接获取元素所有信息

1) 取得特性

例:

1
2
3
var div =  document.getElementById('box');
console.log(div.getAttribute('id')); // 取得id
div.setAttribute('class','myBox'); // 设置class为myBox

2) 创建元素

例:

1
2
3
var div = document.createElement('div'); // 创建div元素
div.id = 'box'; // 设置id
document.body.appendChild(div); // 插入到</body>之前

8.DOM扩展

1) querySelector()和querySelectorAll()可通过Document及Element类型调用,接收一个CSS选择符为参数。 // IE8+支持

前者返回第一个元素,后者返回NodeList的实例(所有匹配到的元素)

2) 元素遍历:

注:对于IE9及之前版本不会返回文本节点,而其他所有浏览器都会返回文本节点,导致在使用childNodes和firstChild等属性的行为不一致。
规范为DOM元素添加了以下5个属性:
childElementCount:返回子元素(不包括文本节点和注释)的个数。
firstElementChild:指向第一个子元素,也就是firstChild的元素版。
lastElementChild:指向最后一个子元素,同理。
previousElementSibling:指向前一个同辈元素,同理。
nextElementSibling:指向后一个同辈元素,同理。

9.DOM扩展与HTML5规范

1) 自定义数据属性

HTML5规定可以为元素添加非标准的属性,要添加前缀data-,目的是为元素提供与渲染无关的信息,或者提供语义信息。

2) 插入标记

innerHTML、innerText

10.元素大小

1) 偏移量:包括元素在屏幕上占用的所有可见的空间,元素的可见大小由其高度、宽度决定,包括内边距、滚动条和边框大小(不包括外边距)

通过以下4个属性可以取得元素的偏移量:
offsetHeight:元素在垂直方向上占用的空间大小,以像素计。包括元素的高度、(可见的)水平滚动条的高度、上下边框高度。
offsetWidth:元素在水平方向上占用的空间大小,以像素计。包括元素的宽度、(可见的)垂直滚动条的宽度、左右边框的宽度。
offsetLeft:元素的左外边框至包含元素的左内边框直接的像素距离。
offsetTop:元素的上外边框至包含与元素的上内边框之间的像素距离。
例:var offsetLeftEle =element.offsetLeft;

2)客户区大小

有关属性有两个:clientWidth和clientHeight
clientWidth属性是元素内容区宽度加上左右内边距宽度。
clientHeight属性是元素内容区高度加上上下内边距高度。
确定浏览器视口大小解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function getViewport() {
if(document.compatMode == 'BackCompat') {
// 首先检查compatMode属性以确定浏览器是否允许在混杂模式,而Safari 3.1之前的版本不支持这个属性则执行else
return {
width:document.body.clientWidth,
height:document.body.clientHeight
}
} else {
// Chrome、Opera和Firefox大多数情况下在标准模式下,因此执行以下语句
return {
width:document.documentElement.clientWidth,
height:document.documentElement.clientHeight
}
}
}

3)滚动大小

四个与滚动大小相关的属性:
scrollHeight:在没有滚动条的情况下,元素内容的总高度。
scrollWidth:在没有滚动条的情况下,元素内容的总宽度。
scrollLeft:被隐藏在内容区域左侧的像素数。
scrollTop:被隐藏在内容区域上方的像素数。
scrollHeight和scrollWidth属性:主要用来确定元素内容的实际大小。
// 注:IE6之前版本运行在混杂模式下时是元素,因此带有垂直滚动条的页面总高度就是 document.documentElement.scrollHeight。
scrollLeft和scrollTop属性:通过设置这其中属性可以改变元素的滚动位置。

函数对象与其他对象的区别

JavaScript中的函数和其他对象最大的区别在于它们有一个特殊的内部属性[[Call]],包含了该函数的执行指令。typeof操作符会在对象内查找这个内部属性,若找到则返回”function”

call() apply() bind() 三者的区别

第一个参数都是传给新函数this的值,call()和apply()的区别仅传人参数形式不同,call()第二个参数起接收参数列表,而apply()第二个参数接收数组或类数组。而bind()跟call()一样,不同的是第二个参数起会被永久设置在新函数中的形参。

JavaScript对象定义属性

当一个属性第一次被添加对象时,JavaScript在对象上调用一个名为[[Put]]的内部方法。[[Put]]方法会在对象上创建一个新节点来保存属性,这个操作不仅指定了初始的值,也定义了属性上的一些特征。
当一个已有属性被赋予新值时,调用的是[[Set]]方法。
delete操作符会对单个对象属性调用名为[[Delete]]内部方法。delete仅对自有属性有效。

查找对象属性

in操作符会检查自有属性和原型属性
hasOwnProperty()只检查自有属性

属性枚举

for-in循环返回的和Object.keys()返回的可枚举属性的区别:for-in循环遍历自有属性和原型属性,而Object.keys()只返回自有属性名字的数组。

属性通用特征

数据和访问器属性都具有[[Enumerable]]和[[Configurable]](布尔值)。如果想改变属性特征,可以使用Object.defineProperty(obj,proName,descript),当调用这个方法时,应该要指定descript所有值,否则布尔类型的值都是false。
定义多重属性可以用Object.defineProperties(obj,proObj)
获取属性特征可以用Object.getOwnPropertyDescriptor(obj,proName)

数据属性特征

[[Value]]所有属性的值都保存在这。
[[Writable]]是一个布尔值,指示该属性是否可以写入。

访问器属性特征

[[Get]]读取属性值 [[Set]]设置属性值;内涵getter和setter函数。

构造函数与原型对象

每个对象在创建时都自动拥有一个构造函数属性,其中包含了一个指向其构造函数的引用。那些通过字面量或者Object构造函数创建的泛用对象,其构造函数属性指向Object,而通过自定义构造函数创建的对象则指向创建它的构造函数。
当调用构造函数时,new会自动创建this对象,且其类型就是构造函数的类型。也可以在构造函数显式调用return。如果返回值是一个对象则返回该对象,否则返回新创建的对象实例。

[[Prototype]]属性

一个对象实例通过内部属性[[Prototype]]跟踪其原型对象。该属性是一个指向该实例使用的原型对象的人指针。当用new创建一个新的对象时,构造函数的原型对象会被赋给该对象[[Prototype]]属性。
avatar
当读取一个对象属性时,JavaScript引擎首先在该对象上的自有属性中查找属性名字。如果找到则返回。如果自有属性中不包含改名字,则会搜索[[Prototype]]中的对象,找到则返回,直至找到末端Object.prototype。
无法给一个对象的原型属性赋值。

对象继承

对象字面量会隐式指定Object.prototype为其[[Prototype]],当然也可以使用Object.create()方法显式指定。第一个参数是需要被设置新对象的[[Prototype]],第二个参数是属性描述对象。也可以使用它来创建一个没有原型链的对象。
var nakedObj = Object.create(null);

Hello csdFe

根据官网安装介绍,本文使用 yum 进行安装
先安装 先决条件:
sudo yum install yum-utils

设置 yum 存储库,创建 /etc/yum.repos.d/nginx.repo ,然后使用以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
[nginx-stable]
name = nginx stable repo
baseurl =
http://nginx.org/packages/centos/$releasever/$basearch/gpgcheck = 1
enabled = 1
gpgkey = https://nginx.org/keys/nginx_signing。 key

[nginx-mainline]
name = nginx mainline repo
baseurl =
http://nginx.org/packages/mainline/centos/$releasever/$basearch/gpgcheck = 1
enabled = 0
gpgkey = https://nginx.org/keys /nginx_signing.key

然后安装:
sudo yum install nginx

过程中选择 Y 回车就完事了

配置 & 启动 Nignx

先找到 Nginx 配置文件 nginx.conf

find /|grep nginx.conf

我找到的目录在 /etc/nginx/nginx.conf

然后 vim /etc/nginx/nginx.conf

在 http {} 里面 ,举例一个基本配置

1
2
3
4
5
6
7
8
server {
listen 80; # 监听端口
server_name localhost; # 监听地址
location / {
root /home/root;
index index.html;
}
}

启动前先打开 防火墙端口 以便外网可以访问 (阿里云貌似默认打开?)

启动 Nginx

whereis nginx

找到启动文件 nginx 在 /usr/sbin/nginx

执行 /usr/sbin/nginx

!重启命令

/usr/sbin/nginx -s reload

! 停止命令

/usr/sbin/nginx -s stop

Done!

1.闭包概念

理解闭包的前提:当某个函数被调用时,会创建一个执行环境及相应的作用域链。然后使用arguments和其他命名参数的值来初始化函数的活动对象。但在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数处于第三位,…直至作为作用域重点的全局执行环境。

2.使用new调用函数或者说发送构造函数调用会自动执行以下操作

  1. 创建一个全新的对象。
  2. 这个新对象会被执行[[原型]]连接。
  3. 这个新对象会绑定到函数调用的this。
  4. 如果函数没有返回其他对象,那么new表达式中的函数调用回自动返回这个对象。

3.js严格模式下this的指向

  1. 全局作用域中的this
    在严格模式下,在全局作用域中,this指向window对象,在函数中的this等于undefined
  2. 对象的函数(方法)中的this
    在严格模式下,对象的函数(方法)中的this指向调用函数的对象实例
  3. 构造函数的this
    在严格模式下,构造函数中的this指向构造函数创建的对象实例
  4. 事件处理函数中的this
    在严格模式下,在事件处理函数中,this指向触发事件的目标对象
    例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function blue_it(e){
    if(this === e.target){
    this.style.backgroundColor = "#00f";
    }
    }
    var elements = document.getElementsByTagName('*');
    for(var i=0 ; i<elements.length ; i++){
    elements[i].onclick = blue_it;
    }
    //这段代码的作用是使被单击的元素背景色变为蓝色

Hello Node.js

广度优先算法

为了解决最短路径的问题。
它可以回答两种问题:

  1. 从节点 A 出发,有前往节点 B 的路径吗?
  2. 从节点 A 触发,前往节点 B 的哪条路径最短?

1.Redux是什么?

官方定义是:对于JavaScript应用而已,Redux是一个可预测状态的“容器”。

设计哲学:

1.数据来源单一性。不论是像计数器一样,还是复杂的聊天系统,我们都使用一个JavaScript对象来表述整个状态机的全部状态,并存储到store当中,而这个store是唯一的。获取状态:let state = store.getState();
2.状态是只读的。即store.getState()返回的结果是只读的,不允许改变它。当页面需要新的数据状态时再生成一颗全新的状态数据树,使得store.getState()返回一个全新的JavaScript对象。Redux规定:当页面需要展现新的数据状态时,我们只需要dispatch一个action即可。
3.使用reducer函数来接收action,并执行页面状态数据树的变更。经过reducer函数处理之后,store.getState()就会返回新页面的数据状态。当然,经过reducer函数根据处理后,返回一个新的JavaScript对象,而不会对原返回值进行更改,因此reducer是一个纯函数。reducer并不直接更改页面的状态数据树,而是根据action产生一颗新的页面状态数,并把它应用在store.getState()当中。
reducer和action需要开发者编写,reducer接收两个参数:当前的页面数据状态和被派发的action
(previousState,action) => newState

2.Redux基本使用和实践

store

store是一个可预测状态的“容器”,保存着整个页面状态数据树,提供了重要的API。
API:

  • dispatch(action):派发action。
  • subscribe(listener):订阅页面数据状态,即store中state的变化。
  • getState:获取当前页面状态数据树,即store中的state。
  • replaceReducer(nextReducer):一般用不到…我也不知道用来干嘛的?

创建store

1
2
3
4
5
import { createStore } from 'redux';
const store = createStore(reducer,preloadedState,enhancer);
// reducer:为开发者编写的reducer函数,必需。
// preloadedState:页面数据状态数的初始数据,可选。
// enhancer:增强器,函数类型,可选。一般接入中间件applyMiddleware(middleware)

action

action描述了状态变更的信息,也就是需要页面做出的变化。这是由开发者定义并借助store.dispatch()派发的。action也是一个对象,Redux规定:action对象需要有一个type属性,作为确定这个action的名称。
action构造器一般为:

1
2
3
4
const actionCreator = data => {
type:'ACTION_TYPE',
data
}

然后使用dispatch派发action,dispatch来自store对象暴露的方法,负责派发action,这个action将作为dispatch的参数。
store.dispatch(actionCreator('这里是数据')); // 派发上面的action

reducer

真正执行action的是reducer(),它必须是一个纯函数,以保证数据变化的可预测性。
reducer一般为:

1
2
3
4
5
6
7
8
9
10
11
12
const updateStateTree = (previousState = {}, action) => {
switch(action.type) {
case 'case1':
return newState1;
case 'case2':
return newState2;
default:
return previousState;
}
}
// previousState为原状态,这里设置为默认值是一个空对象。
// 当无法匹配到的时候,返回原状态。

当多个reducer时,应考虑进行合理拆分。Redux提供了一个工具函数:combineReducers,它接收一个JavaScript对象类型的参数,这个对象的键值分别为页面数据状态分片和子reducer函数,最后合并返回一个reducer。
let resultReducer = combineReducer({reducer1,reducer2});
多个reducer时,往往将reducer命名为其处理的页面状态数据树中的键值,例如有下面的状态数据树:

1
2
3
4
5
const state = {
data1: { ... },
data2: { ... },
data3: { ... }
}

然后reducer命名为:

1
2
3
const data1 = (state.data1,action) => { ... };
const data2 = (state.data2,action) => { ... };
const data3 = (state.data3,action) => { ... };

最后合并reducer:
const resultReducer = combineReducers({data1,data2,data3});

总结

当通过Redux的createStore()创建一个store实例后,我们便可以使用store.dispatch()派发一个action,这个action需要开发者结合自身业务去编写。同时在执行store.dispatch()之后,Redux会“自动”帮我们执行处理变化并更新数据的reducer函数。从store.dispatch()到reducer这个过程可以认为是Redux内部处理的,但具体的action以及reducer需要开发者编写,以完成应用的需求。那么当页面数据状态得到更新之后,实际上就需要store.subscribe(callbackFn)方法订阅数据的更新,并完成UI更新。
avatar

3.Redux开发基础

在Redux架构下保证reducer的不可变性

对于reducer是要保证它不可被修改的,不应该直接更改原有对象或者数组的值,因为他们是引用类型。

1).数组操作

1.增加一项:
显然push()不能满足需求,它会改变原来的数组,可以考虑用**concat()**,它返回一个新的数组。

1
2
3
4
5
6
7
let arr = [1,2,3];
const addArrReducer = (arr,action) => {
return arr.concat(action.data);
}
let newArr = addArrReducer(arr,{type: 'ADD', data:[4]});
console.log(arr); // [1,2,3]
console.log(newArr); // [1,2,3,4]

2.删除一项:
对于删除数组某一项,splice也不能满足需求,它会改变原来的数组,可以考虑用slice()。

1
2
3
4
5
6
7
const rmArrReducer = (arr,index) => {
return [
...arr.slice(0,index),
...arr.slice(index+1)
]
}
// 一般来讲index参数往往出现在action的负载数据中,如action.payload

3.更新一项:
同上面一致使用slice()来更新

1
2
3
4
5
6
7
8
9
const insertOneReducer = (arr,index) => {
return [
...arr.slice(0,index),
arr[index] + 1,
...arr.slice(index+1)
]
}
// 一般来讲index参数往往出现在action的负载数据中,如action.payload
// 某一项加1操作

2).对象操作

1.更新一项
直接修改会违背纯函数原则,一般选择ES Next新特性的Object.assign()来更新。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let item = {
id:0,
book:'JavaScript',
readed:false
}
const setReaded = (item) => {
return Object.assign({},item,{
readed:true
});
}
// 或者使用对象扩展运算符(推荐)
const setReaded = (item) => {
return {
...item,
readed:true
}
}

2.增加一项
与上面同理,一般使用对象扩展运算符。
3.删除一项

1
2
3
4
5
6
7
8
9
10
11
12
let item = {
id:0,
book:'JavaScript',
readed:false,
note:13
}
const newItem = Object.keys(item).reduce((obj,key) => {
if(key !== 'note') {
return { ...obj, [key] : item[key]}
}
return obj
}, {});

4.深入拷贝嵌套数据
需要注意的是:Object.assign()以及扩展运算符等都是浅操作。如果在item外在嵌套一层:

1
2
3
4
5
6
7
8
9
10
11
12
13
let data = {
item1:{
id:0,
book:'javascript',
readed:false
}
}
// 应该需要手动分开所有层分别拷贝,实现一种深拷贝
let item1 = Object.assign({}, data.item1);
let newDate = Object.assign({}, {item1});
newData.item1.readed = true; // 修改内层对象某一项
console.log(data.item1.readed); // false
console.log(newData.item1.readed); // true

当然,也可以自己实现一个深拷贝函数。

Redux中间件和异步

初始Redux中间件

中间件可以在派发任何一个action和执行reducer这两步之间,添加自定扩展功能,例如 异步请求、日志打印等等。
流程如图:
avatar
中间件可以在action到达reducer之前进行日志记录、中断action触发,甚至修改action,或者不进行处理。可以接入多个中间件。
使用:

1
2
3
4
5
6
7
8
9
import { createStore,applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import createLogger from 'redux-logger';
const logger = createLogger();
const store = createStore(
reducer,
applyMiddleware(thunk,logger) // 注意这里的logger要放在所有中间件最后方可生效!
)
// 这里省略了第二个参数,如果有初始化数据,那么applyMiddleware(...arg)将作为第三个参数

Redux的异步处理

dispatch()派发的默认参数只能是一个JavaScript对象,如果要异步处理,则dispatch()接收一个函数为参数,在函数体内进行异步操作,并在异步完成后在派发相应的action。
而redux-thunk中间件正是解决了异步处理问题!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 引入redux创建store和应用中间件等代码省略...
store.dispatch(fetchNewBook('learnRedux'));
function fetchNewBook(book) {
return (dispatch) => {
// 即将请求,可在对应reducer处理渲染“转圈圈”的UI
dispatch({
type:'START_FETCH_NEW_BOOK',
data:book
})
// ajax请求
ajax({
url:`/api/${book}.json`,
type:'POST',
data:{}
}).then(res => {
// 请求成功后reducer根据action.data(即res)来进行页面状态更新
dispatch({
type:'FETCH_NEW_BOOK_SUCCESS',
data:res
})
});
}
}

整体的过程如图:
avatar
redux-thunk对于异步处理的关键在于:使dispatch能够接收异步函数,我们完全可以控制dispatch响应action的时机。

4.结合react使用redux

使用react-redux库

react-redux是对React和Redux进行了连接,对Redux的方法进行了封装和增强,使得使用起来非常方便。
容器组件:指数据状态和逻辑的容器。它并不负责展示,而是只维护内部状态,进行数据分发和处理派发action。因此容器组件对Reudx是感知的,可以使用Redux的API,比如dispatch()等。
展示组件:只负责接收相应的数据,完成页面展示,它本身并不维护数据和状态。实际上,为了渲染页面,展示组件所需要的所有数据都由容器组件通过props层层传递。
avatar

描述 展示组件 容器组件
目的 展示内容 处理数据和逻辑
是否感知Redux 不感知 感知
数据来源 从props获取 从Redux state订阅获取
改变数据 通过回调props派发action 直接派发action
由谁编写 开发者 由react-redux库生产

react-redux有个最重要的方法:connect()
connect([mapStateToProps],[mapDispatchToProps],[mergeProps],[options]);
connect()是用来连接容器组件和展示组件。它的核心是将开发者定义的组件,包装转换生成容器组件。所生成的容器组件能使用Redux store中的那些数据,全由connect()的参数来确定。
一般用法:

1
connect(mapStateToProps,mapDispatchToProps)(presentationalComponent);

第一次执行的第一个参数是一个函数,其作用是给返回的组件注入props,这个props来自Redux store中的状态。所以这个函数一定要返回一个纯JavaScript对象。第一次的第二个参数可以是一个函数也可以是一个对象,如果是一个函数,则这个函数接收dispatch()以及容器的props作为参数,最终也返回一个对象;如果是一个对象,那么兼键值应该是一个函数,用来描述action的生成。第二次执行的参数是接收一个正常的展示组件,并在这基础上返回一个容器组件。
mapStateToProps一般编写形式如下:

1
2
3
4
5
const mapStateToProps = (state) => {
return {
value:state
}
}

它完成从store中选取数据并通过props传递给将要创建的容器组件。

mapDispatchToProps一般编写形式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 接收函数
const mapDispatchToProps = (dispatch,ownProps) => {
return {
onAct:() => dispatch({
type:'CLICK_ACTION',
data:ownProps.data
})
}
}
// 接收对象
const mapDispatchToProps = {
onAct:(data) => {
type:'CLICK_ACTION',
data:data
}
}

mapStateToProps和mapDispatchToProps定义了展示组件需要用到的store内容。其中mapStateToProps负责输入逻辑,就是将状态数据映射到展示组件的参数(props)上;后者负责输出逻辑,即将用户对展示组件的操作映射成action。
两者个参数是可选的。当只有前者的参数时,默认情况下,dispatch()会注入最后返回的容器组件的props中,而只有后者则把前者填上null。两个都忽略时,Redux store的状态数据无法传递下来,因此返回的容器组件就不会监听store的任何变化。
最后,react-redux提供了Provider组件,一般用法是需要将Provider作为整个应用的根组件,并获取store为其prop,以便后续进行下发处理。
所以,在开发中,借助于react-redux的基本模式如下:

1
2
3
4
5
6
let App = connect(mapStateToProps,mapDispatchToProps)(presentationalComponent);
ReactDom.render(
<Provider store={store}>
<App />
</Provider>
);

注:读完《React状态管理与同构实战》第三章的理解

提前储备知识

Array.prototy上常用的方法

比较啰嗦,但可以复习一下!~
添加元素:
push() 方法将一个或多个元素添加到数组的末尾,并返回新数组的长度。
unshift() 方法将一个或多个元素添加到数组的开头,并返回新数组的长度。
删除元素:
pop() 方法从数组中删除最后一个元素,并返回该元素的值。此方法更改数组的长度。
shift() 方法从数组中删除第一个元素,并返回该元素的值。此方法更改数组的长度。
如果在一个空数组上调用pop()或shift(),它返回undefined
在任意位置添加或删除元素:
splice(start,[deleteCount],[itemN]) 方法通过删除现有元素和/或添加新元素来更改一个数组的内容,会直接对数组进行修改。
参数start 指定修改的开始位置(从0计数),如果超出了数组长度,则从数组末尾开始添加内容。如果是负值,则表示从数组末位开始的第几位。如果负数的绝对值大于数组的长度,则表示开始位置为第0位。
deleteCount 可选,整数,表示要移除的数组元素的个数。如果为0则不删除。
itemN 可选,要添加的元素,从start位置开始。如果不指定,则 splice() 将只删除数组元素。

1
2
3
4
let arr = [1,2,3,4];
arr.splice(2,0,'a','b'); // 返回undefined
console.log(arr); // [1,2,'a','b',3,4]
arr.splice(3,2); // ['b',3]

如果是删除则返回被删除的元素组成的数组。
浅拷贝:
slice(start,end) 方法返回一个从开始到结束(不包括结束)选择的数组的一部分浅拷贝到一个新数组对象。返回新元素数组,且原始数组不会被修改。
参数start为开始索引值(从0计数)如果为负数,则从末尾开始,end为结束的索引值(不包括end),如果省略则一直提取到数组末尾。

1
2
let arr1 = [1,2,3,4,5];
arr1.slice(2,4); // [3,4]

concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。
排序元素:
reverse() 方法将数组中元素的位置颠倒,没有参数。第一个数组元素成为最后一个数组元素,最后一个数组元素成为第一个。
sort() 方法用原地算法对数组的元素进行排序,并返回数组。默认排序顺序是根据字符串Unicode码点。

1
2
3
4
let arr = [1,5,3,4,9];
arr.sort((a,b) => {
return a - b; // 从小到大排序
}); // [1, 3, 4, 5, 9]

迭代器函数:

其他:
join() 方法将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串。
indexOf() 方法返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1。

#作用域
JavaScript采用的是词法作用域,是指作用域在函数定义的地方。与之对应的是动态作用域,指作用域在函数调用的地方。
JavaScript中变量是函数级作用域而不是块级作用域(即变量定义的作用域并不是离最近的封闭语句或者代码块,而是包含它们的函数),ES5的try{}catch除外,ES6开始支持。
变量声明提升:函数声明和变量声明总是被JavaScript解释器提升到包含它们的作用域顶部。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function foo() { 
var num;
for(var i=0;i<3;i++) {
num = i;
}
console.log('i',i); // i 3
console.log('num',num); // num 2
}
// 相当于以下
function foo() {
var num,i;
for(i=0;i<3;i++) {
num = i;
}
console.log('i',i); // i 3
console.log('num',num); // num 2
}

要注意变量声明中,赋值是不会提升的(赋值函数也是),仅仅是变量名字被提升了。而函数声明则是整个函数体被提升。
函数声明优先于变量声明。 因为函数在JavaScript是 “一等公民”…

编写习惯

为了减少变量声明混乱,建议编写时候使用“单var”形式。

1
2
3
4
5
function fn() {
var i,
a=3,
b='str';
}

使用IIFE函数创建局部作用域

考虑以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
function fn(a) {
var arr= [],i,n;
for(i=0,n=a.length; i< n;i++) {
arr[i] = function() {
console.log('i',i);
return a[i]
}
}
return arr;
}
var obj = fn([1,2,3,4,5]); // i 5
var f = obj[0];
console.log(f()); // 输出undefined,而不是10

搞清楚这道程序在于理解绑定与赋值的区别。在运行时进入一个作用域,JavaScript会为每一个绑定到该作用域的变量在内存分配一个“槽”,fn函数绑定了三个局部变量即arr、i和n。在循环的每次迭代中,循环体都会为嵌套函数分配一个闭包,而闭包函数里面存的是变量i的引用,由于每一次函数创建后i都发生了变化,因此内部函数最终看到的是i最后的值。(值得注意的是闭包存储的是其外部变量的引用而不是值)
可以改为:

1
2
3
4
5
6
7
8
9
function fn(a) {
var arr= [],i,n;
for(i=0,n=a.length; i< n;i++) {
(arr[j] = function() {
return a[j]
})(i)
}
return arr;
}

但值得注意的是:代码块不能包含break与continue语句,因为在函数外使用它们是不合法的。

总结:

ECMAScript标准里写道:

如果在一个函数中声明变量,这些变量就被定义在了在该函数的函数作用域中。不然它们就是被定义在全局的作用域内(即它们被创建为全局对象的成员),当进入执行环境的时候,变量就被创建。一个语句块不能定义一个新的作用域。只有一个程序或者函数声明能够产生一个新的作用域。创建变量时,被初始化为undefined。如果变量声明语句里面带有赋值操作,则赋值操作只有被执行到声明语句的时候才会发生,而不是创建的时候。