前端学习之路


  • 首页

  • 归档

  • 分类

  • 标签

  • 搜索
close
小朱

小朱

前端学习之路

168 日志
37 分类
37 标签
RSS
GitHub
友情链接
  • 极客学院
  • caniuse
  • codepen
  • JS Bin
  • Babel在线编译
  • Iconfinder图标
  • 在线JSON格式化
  • 智能图像压缩

React Router-基础

发表于 2016-10-29   |   分类于 React Router

React Router

参考:
React Router 使用教程
react-router-tutorial
react-router

基本使用

React-Router 路由库,是官方维护的路由库,事实上也是唯一可选的路由库。它通过管理 URL,实现组件的切换和状态的变化,开发复杂的应用几乎肯定会用到。

一般在src/index.jsx中定义路由,进行整个工程路由的管理。路由匹配规则是从上到下执行,一旦发现匹配,就不再走其余的规则了。

1
2
3
4
5
6
7
8
9
import { Router, Route, hashHistory } from 'react-router';
render((
<Router history={hashHistory}>
<Route path="/" component={App}>
<Route path="/home" component={CommonMainPage} />
</Route>
</Router>
), document.getElementById('app'));

子路由也可以不写在Router组件里面,单独传入Router组件的routes属性。

1
2
3
4
let routes = <Route path="/" component={App}>
<Route path="/home" component={CommonMainPage} />
</Route>;
<Router routes={routes} history={browserHistory}/>

用户访问/home时,会先加载App组件,然后在它的内部再加载CommonMainPage组件。

1
2
3
<App>
<Repos/>
</App>

App组件要写成下面的样子

1
2
3
4
5
6
7
export default React.createClass({
render() {
return <div>
{this.props.children}
</div>
}
})

path属性

Route组件的path属性指定路由的匹配规则。这个属性是可以省略的,这样的话,不管路径是否匹配,总是会加载指定组件。path属性也可以使用相对路径(不以/开头),匹配时就会相对于父组件的路径。嵌套路由如果想摆脱这个规则,可以使用绝对路由。

path可以使用通配符,如:

1
<Route path="/list/:servId" component={ListPage} />

在ListPage组件中通过this.props.params.servId可获取该变量对于在url中的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<Route path="/hello/:name">
// 匹配 /hello/michael
// 匹配 /hello/ryan
<Route path="/hello(/:name)">
// 匹配 /hello
// 匹配 /hello/michael
// 匹配 /hello/ryan
<Route path="/files/*.*">
// 匹配 /files/hello.jpg
// 匹配 /files/hello.html
<Route path="/files/*">
// 匹配 /files/
// 匹配 /files/a
// 匹配 /files/a/b
<Route path="/**/*.jpg">
// 匹配 /files/hello.jpg
// 匹配 /files/path/to/file.jpg

IndexRoute

IndexRoute显式指定根路由的子组件,注意没有路径参数path,即指定默认情况下加载的子组件,如访问路径/,会加载该组件。

1
2
3
4
5
<Router>
<Route path="/" component={App}>
<IndexRoute component={Home}/>
<Route path="accounts" component={Accounts}/>
</Router>

Redirect

Redirect组件用于路由的跳转,即用户访问一个路由,会自动跳转到另一个路由。
<Redirect from="/messages/:id" to="/infos/:id" />

IndexRedirect

IndexRedirect组件用于访问根路由的时候,将用户重定向到某个子组件。
<IndexRedirect to="/welcome" />
用户访问根路径时,将自动重定向到子组件welcome。

Link

Link组件用于取代<a>元素,生成一个链接,允许用户点击后跳转到另一个路由。它基本上就是<a>元素的React 版本,可以接收Router的状态。

1
2
3
4
5
6
7
8
// 点击About会跳转到/about路由
<Link to="/about">About</Link>
// 点击About跳转到/about是文字显示为红色
<Link to="/about" activeStyle={{color: 'red'}}>About</Link>
// 也可以指定class
<Link to="/about" activeClassName="active">About</Link>

在Router组件之外,导航到路由页面,可以使用浏览器的History API。这种方式,对于以前没有访问过的路由是不好使的。

1
2
import { browserHistory } from 'react-router';
browserHistory.push('/some/path');

或者使用context对象。注意,pathname和state名称是固定的,跳转到pathname时会带着state的信息。在跳转进入的组件中,使用this.props.location.state获取这个state信息。

1
2
3
4
5
// 路由跳转
this.context.router.push({
pathname: '/card/' + this.props.servDef.SERV_ID,
state: this.props.listQueryData
})

IndexLink

如果链接到根路由/,不要使用Link组件,而要使用IndexLink组件。这是因为对于根路由来说,activeStyle和activeClassName总是生效,因为’/‘会匹配任何子路由,而IndexLink组件会使用路径的精确匹配。
<IndexLink to="/" activeClassName="active">
或者使用
<Link to="/" activeClassName="active" onlyActiveOnIndex={true}>Home</Link>

histroy

Router组件的history属性,是用来监听浏览器地址栏变化的,并将URL解析成一个地址对象,供 React Router 匹配。history属性,一共可以设置三种值。

  • createMemoryHistory:主要用于服务器渲染,它创建一个内存中的history对象,不与浏览器URL互动,const history = createMemoryHistory(location)

  • hashHistory:路由将通过URL的hash部分#切换,类似example.com/#/some/path

  • browserHistory:不再通过Hash完成,而显示正常的路径example.com/some/path,背后调用的是浏览器的History API。这种情况需要对服务器改造,否则用户直接向服务器请求某个子路由,会显示网页找不到的404错误。
    如果开发服务器使用的是webpack-dev-server,加上–history-api-fallback参数
    webpack-dev-server --inline --content-base . --history-api-fallback,index.html中用绝对路径引入编译后的文件<script src="/bundle.js"></script>

路由的钩子

每个路由都有Enter和Leave钩子,用户进入或离开该路由时触发。

1
2
3
4
<Route path="about" component={About} />
<Route path="inbox" component={Inbox}>
<Redirect from="messages/:id" to="/messages/:id" />
</Route>

如果用户离开/messages/:id,进入/about时,会依次触发以下的钩子:

/messages/:id的onLeave
/inbox的onLeave
/about的onEnter

进行路由跳转

1
2
3
4
<Route
path="messages/:id"
onEnter={({params}, replace) => replace(`/messages/${params.id}`)}
/>

进行登录验证

1
2
3
4
5
6
7
8
9
10
<Route path="/home" component={CommonMainPage} onEnter={requireAuth}>
function requireAuth(nextState, replace) {
if (!storage.get('token')) {
replace({
pathname: '/login',
state: { nextPathname: nextState.location.pathname }
})
}
}

当用户离开一个路径的时候,跳出一个提示框,要求用户确认是否离开。setRouteLeaveHook方法为Leave钩子指定routerWillLeave函数,该方法如果返回false,将阻止路由的切换,否则就返回一个字符串,提示用户决定是否要切换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const Home = withRouter(
React.createClass({
componentDidMount() {
this.props.router.setRouteLeaveHook(
this.props.route,
this.routerWillLeave
)
},
routerWillLeave(nextLocation) {
// 返回 false 会继续停留当前页面,
// 否则,返回一个字符串,会显示给用户,让其自己决定
if (!this.state.isSaved)
return '确认要离开?';
},
})
)

生产环境相关

if-env 依赖: 如果想简化开发、在任何情况运行npm start都执行正确的脚本启动,可以安装if-env依赖npm install if-env --save并在package.json中这样部署,再运行NODE_ENV=production npm start启动

1
2
3
4
5
"scripts": {
"start": "if-env NODE_ENV=production && npm run start:prod || npm start:dev",
"start:dev": "webpack",
"start:prod": "start-cluster"
}

为了限制浏览器可访问的文件,通常创建一个浏览器可访问的文件夹,如public,将index.html、index.css放入该目录,并在server.js中指定可访问的静态资源路径

1
2
3
4
app.use(express.static(path.join(__dirname, 'public')))
app.get('*', function (req, res) {
res.sendFile(path.join(__dirname, 'public', 'index.html'))
})

并指定webpack输入目录也为该目录

1
2
3
4
output: {
path: 'public',
// ...
}

最后启动命令添加参数

1
"start:dev": "webpack-dev-server --inline --content-base public --history-api-fallback",

compression 依赖: 用该中间件压缩和处理静态内容。开启gzip压缩对Web应用会产生巨大影响,当一个gzip压缩浏览器请求某些资源的时候,服务器会在响应返回给浏览器之前进行压缩。

1
2
3
4
var app = express();
// 必须放在第一行
app.use(compression());
...

服务器端渲染:

需要一个服务器渲染的webpack配置webpack.server.config.js,原来的webpack配置webpack.config.js还是保留的

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 fs = require('fs');
var path = require('path');
module.exports = {
entry: path.resolve(__dirname, 'server.js'),
output: {
filename: 'server.bundle.js'
},
target: 'node',
// keep node_module paths out of the bundle
externals: fs.readdirSync(path.resolve(__dirname, 'node_modules')).concat([
'react-dom/server', 'react/addons',
]).reduce(function (ext, mod) {
ext[mod] = 'commonjs ' + mod
return ext
}, {}),
node: {
__filename: true,
__dirname: true
},
module: {
loaders: [
{ test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader?presets[]=es2015&presets[]=react' }
]
}
}

更新package.json文件,用NODE_ENV=production npm start启动

1
2
3
4
5
6
7
8
"scripts": {
"start": "if-env NODE_ENV=production && npm run start:prod || npm run start:dev",
"start:dev": "webpack-dev-server --inline --content-base public/ --history-api-fallback",
"start:prod": "npm run build && node server.bundle.js",
"build:client": "webpack",
"build:server": "webpack --config webpack.server.config.js",
"build": "npm run build:client && npm run build:server"
},

把路由管理放到单独的文件中,以便客户端和服务器端都能获取它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// modules/routes.js
import React from 'react'
import { Route, IndexRoute } from 'react-router'
import App from './App'
import About from './About'
import Repos from './Repos'
import Repo from './Repo'
import Home from './Home'
module.exports = (
<Route path="/" component={App}>
<IndexRoute component={Home} />
<Route path="/repos" component={Repos}>
<Route path="/repos/:userName/:repoName" component={Repo} />
</Route>
<Route path="/about" component={About} />
</Route>
)

1
2
3
4
5
6
7
8
9
10
11
// index.js
import React from 'react'
import { render } from 'react-dom'
import { Router, browserHistory } from 'react-router'
// import routes and pass them into <Router/>
import routes from './modules/routes'
render(
<Router routes={routes} history={browserHistory}/>,
document.getElementById('app')
)
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
// ...
// import some new stuff
import React from 'react'
// we'll use this to render our app to an html string
import { renderToString } from 'react-dom/server'
// and these to match the url to routes and then render
import { match, RouterContext } from 'react-router'
import routes from './modules/routes'
// ...
// send all requests to index.html so browserHistory works
app.get('*', (req, res) => {
// match the routes to the url
match({ routes: routes, location: req.url }, (err, redirect, props) => {
// `RouterContext` is the what `Router` renders. `Router` keeps these
// `props` in its state as it listens to `browserHistory`. But on the
// server our app is stateless, so we need to use `match` to
// get these props before rendering.
const appHtml = renderToString(<RouterContext {...props}/>)
// dump the HTML into a template, lots of ways to do this, but none are
// really influenced by React Router, so we're just using a little
// function, `renderPage`
res.send(renderPage(appHtml))
})
})
function renderPage(appHtml) {
return `
<!doctype html public="storage">
<html>
<meta charset=utf-8/>
<title>My First React Router App</title>
<link rel=stylesheet href=/index.css>
<div id=app>${appHtml}</div>
<script src="/bundle.js"></script>
`
}
var PORT = process.env.PORT || 8080
app.listen(PORT, function() {
console.log('Production Express server running at localhost:' + PORT)
})

Babel-入门

发表于 2016-10-29   |   分类于 ES6

Babel是一个广泛使用的转码器,可以将ES6代码转为ES5代码,从而在现有环境执行。

配置文件

Babel的配置文件是.babelrc,存放在项目的根目录下,该文件用来设置转码规则和插件。

1
2
3
4
{
"presets": [], -- 设定转码规则
"plugins": [] -- 设定支持的插件,如'antd'
}

ES2015转码规则
npm install --save-dev babel-preset-es2015
react转码规则
npm install --save-dev babel-preset-react
ES7不同阶段语法提案的转码规则(共有4个阶段),选装一个
npm install --save-dev babel-preset-stage-0
npm install --save-dev babel-preset-stage-1
npm install --save-dev babel-preset-stage-2
npm install --save-dev babel-preset-stage-3

babel-cli

npm install --global babel-cli 用于命令行转码的工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 转码结果输出到标准输出
$ babel example.js
# 转码结果写入一个文件
# --out-file 或 -o 参数指定输出文件
$ babel example.js --out-file compiled.js
# 或者
$ babel example.js -o compiled.js
# 整个目录转码
# --out-dir 或 -d 参数指定输出目录
$ babel src --out-dir lib
# 或者
$ babel src -d lib
# -s 参数生成source map文件
$ babel src -d lib -s

babel-cli工具自带一个babel-node命令,提供一个支持ES6的REPL环境。它支持Node的REPL环境的所有功能,而且可以直接运行ES6代码。

babel-register

babel-register模块改写require命令,为它加上一个钩子。此后,每当使用require加载.js、.jsx、.es和.es6后缀名的文件,就会先用Babel进行转码。注意,它只会对require命令加载的文件转码,而不会对当前文件转码。另外,由于它是实时转码,所以只适合在开发环境使用。

babel-core

如果某些代码需要调用Babel的API进行转码,就要使用babel-core模块,配置对象为options。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var babel = require('babel-core');
// 字符串转码
babel.transform('code();', options);
// => { code, map, ast }
// 文件转码(异步)
babel.transformFile('filename.js', options, function(err, result) {
result; // => { code, map, ast }
});
// 文件转码(同步)
babel.transformFileSync('filename.js', options);
// => { code, map, ast }
// Babel AST转码
babel.transformFromAst(ast, code, options);
// => { code, map, ast }

babel-polyfill

Babel默认只转换新的JavaScript句法(syntax),而不转换新的API,比如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局对象,以及一些定义在全局对象上的方法(比如Object.assign)都不会转码。如果想让这些代码执行,必须使用babel-polyfill,为当前环境提供一个垫片。

浏览器环境

Babel也可以用于浏览器环境。可在网页中插入:

1
2
<script src="node_modules/babel-core/browser.js"></script>
<script type="text/babel">

或

1
2
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.4.4/babel.min.js"></script>
<script type="text/babel">

网页中实时将ES6代码转为ES5,对性能会有影响。生产环境需要加载已经转码完成的脚本。

Fetch替代Ajax

发表于 2016-10-22   |   分类于 JavaScript

Fetch出现原因

传统 Ajax 已死,Fetch 永生 传统 Ajax 指的是 XMLHttpRequest(XHR)

XMLHttpRequest 是一个设计粗糙的 API,不符合关注分离(Separation of Concerns)的原则,配置和调用方式非常混乱,而且基于事件的异步模型写起来也没有现代的 Promise,generator/yield,async/await 友好。Fetch 的出现就是为了解决 XHR 的问题,它是替代XMLHttpRequest用来发送网络请求的非常新的API。由于目前大多数浏览器原生还不支持它,建议你使用 isomorphic-fetch 库。

使用async/await对fetch的使用进行优化后,写异步代码就像写同步代码一样爽。async/await是非常新的 API,属于 ES7。

Promise,generator/yield,await/async都是现在和未来JS解决异步的标准做法,可以完美搭配使用。另外,Fetch 也很适合做现在流行的同构应用。同构(isomorphic/universal)就是使前后端运行同一套代码的意思,后端一般是指 NodeJS 环境。

Fetch 优点主要有:

语法简洁,更加语义化
基于标准 Promise 实现,支持 async/await
同构方便,使用 isomorphic-fetch

支持状况及解决方案

原生支持率并不高,幸运的是,引入下面这些 polyfill 后可以完美支持 IE8+ :

由于 IE8 是 ES3,需要引入 ES5 的 polyfill: es5-shim, es5-sham
引入 Promise 的 polyfill: es6-promise
引入 fetch 探测库:fetch-detector
引入 fetch 的 polyfill: fetch-ie8
可选:如果你还使用了 jsonp,引入 fetch-jsonp
可选:开启 Babel 的 runtime 模式,现在就使用 async/await

JSONP(JSON with Padding)是JSON的一种”使用模式”,可用于解决主流浏览器的跨域数据访问的问题
jsonp详解

NPM-基础

发表于 2016-10-20   |   分类于 NPM

常用命令

npm search antd # 在已发布模块的name、tags、description字段中搜索关键字
npm view antd # 看该模块的package.json文件以及与NPM仓库相关的属性
npm list antd # 查看本地antd模块版本
npm root [-g] # 查看项目或全局安装路径
which node # 查看本地 node 安装路径
npm init # 初始化 package.json
npm install [name@version][-g]/[–save][-dev] # 装包
npm uninstall [name@version][-g]/[–save][-dev] # 卸载
npm get registry # 查询当前配置的镜像
npm config set registry http://registry.npm.taobao.org/ # 设置镜像

npm scripts 使用指南

JavaScript-基础

发表于 2016-10-20   |   分类于 JavaScript

JavaScript是单线程、基于原型、面向对象、弱类型的动态脚本语言。6种数据类型,其中包括5个基本数据类型:String、Number、Boolean、Null、Undefined,1个复杂数据类型:Object。
JavaScript API

typeof

通过typeof进行数据类型的判断,是一个一元运算,返回值为字符串,取值如下:

number 表示数字
string 表示字符串
boolean 表示布尔
object 表示对象,null、对象、数组返回的都是object类型
symbol 表示Symbol
undefined 表示Undefined
function 表示为一个函数

instanceof

是一个三元运算,用于判断一个对象是否是另一个对象的实例,可以在继承关系中用来判断一个实例是否属于它的父类型。

类型判断

字符串常量是基本类型string,new String()生成的字符串是对象类型。

1
2
3
4
5
6
7
typeof 'a' // 'string'
typeof new String('a') // 'object'
'a' instanceof String // false
new String('a') instanceof String // true
typeof null == 'object' // true
// 在浏览器环境中,通常要查看对象内部的[[Class]]值
Object.prototype.toString.call(a) // '[object String]'

null、undefined、’’、0 在条件表达式中被判定为false

1
2
3
4
5
6
typeof undefined // undefined
undefined instanceof Object // false
typeof null // 'object'
null instanceof Object // false
undefined == null // true
undefined === null // false

undefined 和 null

null和undefined基本是同义的,只有一些细微的差别。

null表示”没有对象”,即该处不应该有值。典型用法是:

  • 做为函数的参数,表示该函数的参数不是对象
  • 作为对象原型链的终点,Object.getPrototypeOf(Object.prototype) 为 null

undefined表示”缺少值”,就是此处应该有一个值,但是还没有定义。典型用法是:

  • 变量被声明了,但没有赋值时,就等于undefined
  • 调用函数时,应该提供的参数没有提供,该参数等于undefined
  • 对象没有赋值的属性,该属性的值为undefined
  • 函数没有返回值时,默认返回undefined

函数参数数量

函数名.length 获取函数定义中的参数个数

1
2
var fun = function (arg1, arg2) {};
fun.length; // 2

判断数组的方法

Array.isArray(a) === true
a.constructor === Array
Object.prototype.toString.call(a) === ‘[object Array]’
a instanceof Array

Array API

array.push() – 将一个或多个新元素添加到数组结尾,返回数组新长度
array.unshift() – 将一个或多个新元素添加到数组开始,返回数组新长度
array.splice(start, count, item1, …, itemN) – 删除从第一个数为起始、第二个参数为个数的这些元素后,在被截取掉的地方追加一个或多个从第三个参数开始的新元素,操作数组本身
array.pop() – 移除最后一个元素并返回该元素值
array.shift() – 移除最前一个元素并返回该元素值
array.slice(start, end) – 返回第一个参数起始、第二个参数截至、左闭右开的数组,不操作数组本身
array.concat() – 将两个数组进行合并,返回数据合并的结果,参数可为元素序列或数组,不操作数据本身
array.join() – 将数组元素用参数中的符号连接起来,返回一个字符串
array.sort() – 排序,可将排序方法以参数传入,如没有参数,按字符串正序排序的,操作数组本身
array.reverse() – 逆序排序,操作数组本身
array.indexOf() – 一个参数时,返回元素在数据中第一次出现的索引,比较时进行的是”===”的比较;两个参数时,第一个参数为起始位置,第二个参数为查询元素;
array.lastIndexOf() – 类似indefOf,区别是从找到的是数组中最后一个出现的该元素的索引
array.every(function(item, index, array){}) – 对数组的每一个元素进行函数的运行,如果函数返回的都是true,最后则返回true;如果有一个元素返回false,最后则返回false (注意,return false可终止循环)
array.some(function(item, index, array){}) – 对数组的每一个元素进行函数的运行,如果函数有一个返回的是true,最后则返回true;如果都返回false,最后则返回false
array.filter(function(item, index, array){}) – 对数组的每一个元素进行函数的运行,将函数返回值为false的过滤掉返回
array.forEach(function(item,index,array){}) – 对数组的每一个元素进行函数的运行
array.map(function(item,index,array){}) – 对数组的每一个元素进行函数的运行,把函数每次运行的结果按序组成的新数组返回
array.reduce(function(prev,cur,index,array){}) – 调用一个函数,pre为上一次调用回调返回的值或者是提供的初始值,第二个参数为当前被处理的元素,第三个参数为当前位置,第四个参数为数组,依次将每次调用的结果替换掉数据中的元素,最后返回一个值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var arr = [0, 1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9]; // [0, 1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9]
arr.push(10, 11); // 返回12, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
arr.unshift(-2, -1); // 返回14,[-2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
arr.pop(); // 返回11,[-2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
arr.pop(); // 返回10,[-2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
arr.shift(); // 返回-2,[-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
arr.shift(); // 返回-1,[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
arr.slice(1, 3); // 返回[1, 2],[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
arr.splice(1, 2, 11, 12); // 返回[1, 2],[0, 11, 12, 3, 4, 5, 6, 7, 8, 9]
arr.splice(1, 2, 1, 2, 3); // 返回[11, 12],[0, 1, 2, 3, 3, 4, 5, 6, 7, 8, 9]
arr.splice(3, 1); // 返回[3],[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
arr.concat([-2, -1]); // 返回[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -2, -1],[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
function sortFun(a, b) {
return a - b;
}
arr.sort(sortFun); // 返回[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
arr.reverse(); // 返回[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],[9, 8, 7, 6, 5, 4, 3, 3, 2, 1, 0]
arr.reduce(function(prev, cur, index, array){return prev + cur;}); // 返回45
arr.reduce(function(prev, cur, index, array){return prev + cur;}, 1); // 返回46

详见Array API

String API

String.fromCharCode(num1, …, numN) – 返回使用指定的Unicode值序列创建的字符串
String.raw(callSite, …substitutions) – 获取一个模板字符串的原始字面量值的,callSite是一个模板字符串,substitutions表示任意个内插表达式对应的值
str.charAt(index) – 返回指定位置的字符
str.charCodeAt(index) – 返回指定位置字符的UTF-16代码单元值的数字
str.concat(str2, …, strN) –返回原字符串拼接一个或多个字符串的新字符串
str.slice(start, end) – 返回start开始、end结束、左闭右开的字符串
str.substring(start, end) – 返回较小值开始、较大值结束、左闭右开的字符串
str.substr(start, count) – 返回start开始的count个字符组成的字符串
str.includes(searchStr[, position]) – 判断从position位置开始包含position是否存在searchStr字符串
str.indexOf(searchValue[, fromIndex]) – 返回从fromIndex开始第一次出现searchValue的位置索引,没有返回-1
str.lastIndexOf(searchValue[, fromIndex]) – 返回从fromIndex从后向前查找,第一次出现searchValue的位置索引,没有返回-1
str.localeCompare() – 返回一个数字(1或-1或0)来指示一个参考字符串是否在排序顺序在前面或后面或与给定字符串相同
str.repeat() – 返回包含被连接在一起的指定数量的字符串的副本
str.match(regexp) – 返回一个包含了整个匹配结果以及任何括号捕获的匹配结果的 数组,如果没有匹配返回null
str.replace(regexp|substr, newSubStr|function) – 返回一个由替换值替换一些或所有匹配的模式后的新字符串,模式可以是一个字符串或者一个正则表达式,function返回值将替换掉第一个参数匹配到的结果
str.search(regexp) – 执行正则表达式和String对象之间的一个匹配搜索的索引,没有返回-1
str.trim() – 返回删除两端空白字符的字符串
str.trimLeft() – 返回删除左端空白字符的字符串
str.trimRight() – 返回删除右端空白字符的字符串
str.toLocaleLowerCase() – 返回使用本地化映射规则将字符串转化成小写形的式
str.toLocaleUpperCase() – 返回使用本地化映射规则将字符串转化成大写的形式
str.toLowerCase() – 返回将字符串转化成小写的形式
str.toUpperCase() – 返回将字符串转化成大写的形式
str.toString() – 返回指定对象的字符串形式
str.valueOf() – 返回一个String对象的原始值
str.split([separator[, limit]]) – 返回使用separator分隔字符串组成的数组,limit限定返回的分割片段数量

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
var str = 'abcdefghijklmnopqrstuvwxyz';
var str2 = String.fromCharCode(65, 66, 67); // 返回ABC
String.raw `Hi\n!`; // 返回'Hi\n!'
str.charAt(3); // 返回'd'
str.charCodeAt(3); // 返回100
str.concat(str2); // 返回'abcdefghijklmnopqrstuvwxyzABC'
str.slice(1,5); // 返回'bcde'
str.substring(1, 3); // 返回'bc'
str.substring(3, 1); // 返回'bc'
str.substr(1, 3); // 返回'bcd'
str.includes('xyz'); // 返回true
str.includes('xyz', 24); // 返回false
str.indexOf('xyz'); // 返回23
str.indexOf('xyz', 24); // 返回-1
'check'.localeCompare('against'); // 返回1
'against'.localeCompare('check'); // 返回-1
'check'.localeCompare('check'); // 返回0
str.repeat(2); // 返回'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz'
var str3 = 'number in number';
str3.match('n'); // 返回["n", index: 0, input: "number in number"]
str3.match(/n/); // 返回["n", index: 0, input: "number in number"]
str3.match(/n/g); // 返回["n", "n", "n"]
str.replace('abc', '123'); // 返回'123defghijklmnopqrstuvwxyz'
str.replace('abc', () => '123'); // 返回'123defghijklmnopqrstuvwxyz'
str.search('b'); // 返回1
str.toLocaleLowerCase(); // 返回'abcdefghijklmnopqrstuvwxyz'
str.toLocaleUpperCase(); // 返回'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
str.toLowerCase(); // 返回'abcdefghijklmnopqrstuvwxyz'
str.toUpperCase(); // 返回'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
var num = 1234;
num.toString(); // 返回'1234'
'a|b|c|d|e|f|'.split('|'); // 返回 ["a", "b", "c", "d", "e", "f", ""]
'a|b|c|d|e|f|'.split('|', 3); // 返回 ["a", "b", "c"]

详见String API

Object.freeze(浅冻结)

Object.freeze(object)函数执行下面的操作:

使对象不可扩展,无法向其添加新属性
为对象的所有属性将 configurable 特性设置为 false。在 configurable 为 false 时,无法更改属性的特性且无法删除属性
为对象的所有数据属性将 writable 特性设置为 false。当 writable 为 false 时,无法更改数据属性值

实现深冻结

1
2
3
4
5
6
7
8
9
function deepFreeze(obj) {
var propNames = Object.getOwnPropertyNames(obj);
propNames.forEach(function(name) {
var prop = obj[name];
if (typeof prop == 'object' && prop !== null)
deepFreeze(prop);
});
return Object.freeze(obj);
}

函数表达式没有变量提升

在JavaScript中,变量和函数的声明都会提升到作用域的最前面。函数表达式并没有被提升,这也是函数表达式与函数声明的区别。

1
2
3
4
5
6
(function(){
f1(); // ReferenceError: f1 is not a function
f2();
var f1 = function(){};
function f2(){}
})();

上面代码中函数声明f2被提升,所以在前面调用f2是没问题的。虽然变量f1也被提升,但f1提升后的值为undefined,其真正的初始值是在执行到函数表达式处被赋予的。所以只有声明是被提升的。

try、catch、finally

1
2
3
4
5
6
7
try {
tryStatements // 尝试执行代码块
} catch (exception) {
catchStatements// 捕获错误的代码块
} finally {
finallyStatements// 无论try/catch结果如何都会执行的代码块
}

__defineGetter__、__defineSetter__

类似面向对象语言中的get和set关键字

有两种方法来定义Getter或Setter方法:

1.在对象初始化时定义
2.在对象定义后通过在原型上定义__defineGetter__、__defineSetter__的方法来追加定义

改变上下文的方式

call – fun.call(context, arg1, … , argn)
applay – fun.call(context, [arg1, … , argn])
bind – fun.bind(context, arg1, … , argn)

作用域和作用域链

作用域

作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。在JavaScript中,变量的作用域有全局作用域和局部作用域两种。

全局作用域:

在代码中任何地方都能访问到的对象拥有全局作用域
最外层函数和在最外层函数外面定义的变量拥有全局作用域
所有末定义直接赋值的变量自动声明为拥有全局作用域
所有window对象的属性拥有全局作用域

局部作用域:

局部作用域一般只在固定的代码片段内可访问到
函数内部定义的变量和函数,也会看到有人把这种作用域称为函数作用域

作用域链

在JavaScript中,函数也是对象。函数对象和其它对象一样,拥有可以通过代码访问的属性和一系列仅供JavaScript引擎访问的内部属性。其中一个内部属性是[[Scope]],由ECMA-262标准第三版定义,该内部属性包含了函数被创建的作用域中对象的集合,这个集合被称为函数的作用域链,它决定了哪些数据能被函数访问。

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 g = '全局变量';
var f1 = function() {
debugger;
var part1 = '局部变量1';
var f2 = function() {
debugger;
var part2 = '局部变量2';
}
f2();
}
f1();
var g = '全局变量';
function f1() {
debugger;
var part1 = '局部变量1';
function f2() {
debugger;
var part2 = part1;
}
f2();
}
f1();

在函数执行时,会创建一个称为“运行期上下文(execution context)”的内部对象,运行期上下文定义了函数执行时的环境。每个运行期上下文都有自己的作用域链,用于标识符解析,当运行期上下文被创建时,而它的作用域链初始化为当前运行函数的[[Scope]]所包含的对象。这些值按照它们出现在函数中的顺序被复制到运行期上下文的作用域链中。它们共同组成了一个新的对象,叫“活动对象(activation object)”,该对象包含了函数的所有局部变量、命名参数、参数集合以及this,然后此对象会被推入作用域链的前端,当运行期上下文被销毁,活动对象也随之销毁。

在函数执行过程中,每遇到一个变量,都会经历一次标识符解析过程以决定从哪里获取和存储数据。该过程从作用域链头部,也就是从活动对象开始搜索,查找同名的标识符,如果找到了就使用这个标识符对应的变量,如果没找到继续搜索作用域链中的下一个对象,如果搜索完所有对象都未找到,则认为该标识符未定义。

一般情况下,在运行期上下文运行的过程中,其作用域链只会被 with 语句和 catch 语句影响。当代码运行到with语句时,运行期上下文的作用域链临时被改变了。一个新的可变对象被创建,它包含了参数指定的对象的所有属性。这个对象将被推入作用域链的头部,这意味着函数的所有局部变量现在处于第二个作用域链对象中,因此访问代价更高了。因此在程序中应避免使用with语句。

另外一个会改变作用域链的是try-catch语句中的catch语句。当try代码块中发生错误时,执行过程会跳转到catch语句,然后把异常对象推入一个可变对象并置于作用域的头部。在catch代码块内部,函数的所有局部变量将会被放在第二个作用域链对象中。一旦catch语句执行完毕,作用域链即会返回到之前的状态。

闭包

Javascript语言,函数内部可以直接读取全局变量,在函数外部无法读取函数内的局部变量。如果要得到函数f1内的局部变量,就需要在函数f1的内部,再定义一个函数f2,f2可以读取f1中的局部变量,只要把f2返回,就可以在f1外部读取它的内部变量了。这样的f2函数,就是闭包。闭包的定义是“能够读取其他函数内部变量的函数”,其实简单理解就是“一个定义在函数中的函数”。注意,f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 函数内部可以直接读取全局变量,在函数外部无法读取函数内的局部变量
var g = '全局变量';
function f1() {
console.log(g);
var part = '局部变量';
}
// part is not defined
console.log(part);
// 使用闭包
function f1() {
var part = '局部变量';
function f2() {
return part;
}
return f2;
}
var res1 = f1();
var partW = res1();
console.log(partW);

闭包可以用在许多地方,它的最大用处有两个:一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。

使用闭包的注意点:
1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露,解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象使用,把闭包当作它的公用方法,把内部变量当作它的私有属性,这时一定要小心,不要随便改变父函数内部变量的值。

原型、原型链

理解JavaScript原型
彻底理解JavaScript原型

原型

原型是一个对象,所有对象都可以成为原型,其它对象可以通过它实现属性继承。原型真正魅力体现在多个实例共用一个通用原型的时候。如果试图获取一个主数据类型的原型时,它会被强制转化成了一个对象。

构造函数提供了一种方便的跨浏览器机制,这种机制允许在创建实例时为实例提供一个通用的原型。函数A的原型属性(prototype property )是一个对象,当这个函数被用作构造函数来创建实例时,该函数的原型属性将被作为原型赋值给所有对象实例,即所有实例的原型引用的是函数的原型属性。即所有实例的原型引用的是函数的原型属性。

如果修改了构造函数的原型属性,那么已经存在的该构造函数的实例将获得构造函数的最新版本。
如果修改了构造函数的原型属性所指向的对象,那么新创建实例时实例原型所指向的是新对象,但是这并不会影响已经创建的实例的原型。

要注意区分原型属性和原型对象:

object.prototype – 原型属性
Object.getPrototypeOf(object) 、object.__proto__ 、object.constructor.prototype – 原型对象,真正的原型
constructor – 是原型对象的属性,对应创建所有指向该原型的实例的构造函数
对于所有对象,都有__proto__属性,这个属性对应该对象的原型
对于所有函数对象,除了__proto__属性之外,还有prototype属性,当一个函数被用作构造函数来创建实例时,该函数的prototype属性值将被作为原型赋值给所有对象实例(也就是设置实例的__proto__属性)

通过修改原型可以达到继承的效果,如:

1
2
3
var a = {};
a.__proto__ = Array.prototype;
a.length; // 0

原型链

所有的对象在默认的情况下都有一个原型,因为原型本身也是对象,所以每个原型自身又有一个原型,对象的原型指向对象的父,而父的原型又指向父的父,我们把这种通过原型层层连接起来的关系撑为原型链。这条链的末端一般总是默认的对象原型。

所有对象的原型都将追溯到”Object{}”对象,Object对象本身是一个函数对象。如果a的原型属于A的原型链,表达式 a instance of A 值为true。

原型的继承机制是发生在内部且是隐式的。当想要获得一个对象a的属性foo的值,javascript会在原型链中查找foo的存在,如果找到则返回foo的值,否则undefined被返回。如果想要覆盖原型链上的一些属性,就可以直接在对象中定义这些属性。

hasOwnProperty是Object.prototype的一个方法,该方法能判断一个对象是否包含自定义属性而不是原型链上的属性,hasOwnProperty是JavaScript中唯一一个处理属性但是不查找原型链的函数。

二维作用域链查找:当代码需要查找一个属性或者描述符的时候,首先会通过作用域链来查找相关的对象;一旦对象被找到,就会根据该对象的原型链来查找属性。

严格模式

详见 Javascript 严格模式详解

DOM和BOM

参考 DOM与BOM 和 BOM和DOM详解

javascript 有三部分构成:ECMAScript,DOM和BOM。根据宿主(浏览器)的不同,具体的表现形式也不尽相同,ie和其他的浏览器风格迥异。

核心(ECMAScript):描述了JS的语法和基本对象
文档对象模型(DOM):描述了处理网页内容的方法和接口,Document对象
浏览器对象模型(BOM):描述了与浏览器交互的方法和接口,Window对象

DOM(Document Object Model)

DOM(文档对象模型)是W3C的标准,所有浏览器公共遵守的标准。
DOM 是HTML和XML的应用程序接口(API),就是把「文档」当做一个「对象」来看待。
DOM 使程序和脚本有能力动态地访问和更新文档的内容、结构以及样式,是平台和语言中立的接口。

BOM(Browser Object Model)

BOM(浏览器对象模型)是各个浏览器厂商根据DOM在各自浏览器上的实现,表现为不同浏览器定义有差别,实现方式不同。
BOM 就是把「浏览器」当做一个「对象」来看待,BOM除了可以访问文档中的组件之外,还可以访问浏览器的组件。
BOM 定义了JavaScript可以进行操作的浏览器的各个功能部件的接口,主要处理浏览器窗口和框架,不过通常浏览器特定的JavaScript扩展都被看做BOM的一部分,这些扩展包括:

弹出新的浏览器窗口
移动、关闭浏览器窗口以及调整窗口大小
提供Web浏览器详细信息的定位对象
提供用户屏幕分辨率详细信息的屏幕对象
对cookie的支持
IE扩展了BOM,加入了ActiveXObject类,可以通过JavaScript实例化ActiveX对象

注意:window 是 BOM 对象,而非 js 对象

关系

Javacsript是通过访问BOM对象来访问、控制、修改客户端(浏览器),由于BOM的window包含了document,因此可以直接使用window对象的document属性来访问、检索、修改XHTML文档内容与结构。因为document对象又是DOM模型的根节点,也就是DOM的最根本的对象是BOM的window对象的子对象,可以说,BOM包含了DOM,浏览器提供出来给予访问的是BOM对象,从BOM对象再访问到DOM对象,从而js可以操作浏览器以及浏览器读取到的文档。
区别:DOM描述了处理网页内容的方法和接口,BOM描述了与浏览器进行交互的方法和接口。

获取字符串实际长度(包含汉字)

方法一

1
2
3
4
5
6
7
8
9
10
11
12
function getStrLength1 (str) {
// 获得字符串实际长度,中文2,英文1
var realLength = 0, len = str.length, charCode = -1;
for (var i = 0; i < len; i++) {
charCode = str.charCodeAt(i);
if (charCode >= 0 && charCode <= 128)
realLength += 1;
else
realLength += 2;
}
return realLength;
};

方法二

1
2
3
4
5
6
7
function getStrLength2 (str) {
// 先把中文替换成两个字节的英文,再计算长度
// [\u0391-\uFFE5]匹配双字节字符(汉字+符号)
// str.replace(/[^\x00-\xff]/g, 'aa');
// [^\x00-\xff],匹配双字节字符,检查是否是汉字或者全角
return str.replace(/[\u0391-\uFFE5]/g,"aa").length;
};

关联数组

关联数组是指每个元素自定义字符串的下标的数组,由于是自定义的下标所以关联数组的length属性没有效果。

1
2
3
4
5
6
7
// 直接定义数组
myhash = {"key1": "val1", "key2": "val2" }
// 用Array 定义数组
myhash = new Array();
myhash["key1"] = "val1";
myhash["key2"] = "val2";

感觉关联数组和对象基本上一样,目前只看到可以用obj.key1引用对象的属性,但不能myhash.key1这样引用数组的元素这一个区别。

对象遍历

发现javascript中的对象,如果key值为整数或整数字符,则该对象会的属性会按key值排序,使用时要注意。

JSON对象

JSON.stringify(value [, replacer] [, space])

  • value:是必选字段。就是你输入的对象,比如数组,类等。
  • replacer:这个是可选的。它又分为2种方式,一种是数组,第二种是方法。
    • 情况一:replacer为数组时,通过后面的实验可以知道,它是和第一个参数value有关系的。一般来说,系列化后的结果是通过键值对来进行表示的。 所以,如果此时第二个参数的值在第一个存在,那么就以第二个参数的值做key,第一个参数的值为value进行表示,如果不存在,就忽略。
    • 情况二:replacer为方法时,那很简单,就是说把系列化后的每一个对象(记住是每一个)传进方法里面进行处理。
  • space:就是用什么来做分隔符的。
    • 如果省略的话,那么显示出来的值就没有分隔符,直接输出来 。
    • 如果是一个数字的话,那么它就定义缩进几个字符,当然如果大于10 ,则默认为10,因为最大值为10。
    • 如果是一些转义字符,比如“\t”,表示回车,那么它每行一个回车。
    • 如果仅仅是字符串,就在每行输出值的时候把这些字符串附加上去。当然,最大长度也是10个字符。

JSON.parse()

将字符串转为json对象,不允许有尾逗号

操作event对象

在我的项目中,想要点击Grid行的时候有点击行首部Checkbox的效果,对我来说最简单的方式就是在点击行的事件中调用点击Checkbox的方法handleChecked,但handleChecked需要event参数并且要取event.target.checked值。于是我克隆了一个event对象,显示的给它的target.checked赋值,并使用下面的语句给event的target赋checked属性,即可达到想要的效果。

1
2
event.target.setAttribute('checked', true);
event.target.getAttribute('checked', true);

setTimeout/setInterval 第三个参数

setTimeout/setInterval 常用的都是传入两个参数,callback 函数和 time 延迟时间,第三个参数开始以及后面的参数,都会以作为参数传入到 callback 函数中。

1
2
3
4
function func(arg1, arg2) {
console.log(arg1, arg2);
}
setTimeout(func, 1000, 'first', 'second'); // 输出 first second

如何让setTimeout立即执行

注意,不是time, 而是time(), 这样浏览器解码的时候扫到这一行的代码直接就执行了。

1
2
3
4
function time(){
console.log("it's A good question");
}
setTimeOut( time(), 100000 );

实现鼠标双击事件不触发单击事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var timeFunName = null;
// 单击事件处理
$("button").bind("click", function () {
// 取消上次延时未执行的方法
clearTimeout(timeFunName);
// 延时300毫秒执行单击
timeFunName = setTimeout(function () {
// TODO 执行单击操作
}, 300);
});
// 双击事件处理
$("button").bind("dblclick", function () {
// 取消上次延时未执行的方法
clearTimeout(timeFunName);
// TODO 执行双击操作
});

new关键字

1
2
3
4
5
function FuncA(name) {
this.name = name;
};
var a = new FuncA('this is functon A');
console.log(a.name); // 输出 this is functon A

new操作在背后做了如下操作。

1
2
3
4
5
6
7
8
9
10
11
// 创建一个object,假设叫x
var x = {};
// 把A.prototype赋给x.__proto__
x.__proto__ = FuncA.prototype;
// 调用A.call(x)
var result = FuncA.call(x);
// 如果result是对象则返回,否则返回空对象x
if (typeof(result) === "object"){
return result;
}
return x;

因此也有:

1
2
3
4
5
6
7
8
9
10
11
12
function FuncB() {
return {
name: 'name'
};
}
var b = new FuncB();
console.log(b.name); // 输出 name
function FuncC() {
return 'name';
}
var c = new FuncC();
console.log(c); // 输出 空对象

字符串转数字

parseInt将字符串(String)类型转为整数类型,结果是整数或者NaN。从头解析string为整数,在遇到不能解析的字符时就返回已经解析的整数部分,如果第一个字符就不能解析,就直接返回NaN。只有对String类型调用这些方法,这两个函数才能正确运行;对其他类型返回的都是NaN。

1
2
parseInt('123abc'); // 123
parseInt('8e-10'); // 8

Number() 函数,不用new操作符,把对象(Object)的值转换为数字,结果是数字(整数,小数等)或者NaN。如果使用new操作符,返回的是Number对象。如果无法转换为数字,就返回NaN。强制类型转换可以访问特定的值,即使它是另一种类型的。

1
2
Number('123abc'); // NaN
Number('8e-10'); // 8e-10

+ 操作符可以将字符串转化为数字,如 +'123' 返回是123。和 Number() 一样,+'123a' 返回是 NaN;Number() 和 + 转换数字速度要比 parseInt() 快的多。

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

CSS-选择器

发表于 2016-10-15   |   分类于 CSS

通配选择器

*{ margin:0; padding: 0; }

元素选择器

div { background: grey; }

ID选择器

#id { background: grey; }

类选择器

.class { background: grey; }
.class1.class2 { background: grey; } // 需要同时具有指定的类才能匹配

群组选择器

selector1,selector2 { background: grey; } // 匹配为selector1和selector2的所有元素

后代选择器

E F { background: grey; } // 匹配E的元素包含的所有匹配F的元素

子选择器

E>F { background: grey; } // 匹配E的元素包含的匹配F的直接子元素

相邻兄弟选择器

E+F { background: grey; } // 匹配E的元素后紧挨着的匹配F的元素

通用选择器

E~F { background: grey; } // 匹配E的元素后的所有匹配F的元素

动态伪类选择器

:link 未被访问过的超链接
:visited 已被访问过的超链接
:hover 鼠标停留
:ative 被激活,常用于锚点和按钮上
:focus 获得焦点

锚点伪类的设置必须遵守”link-visited-hover-active”

目标伪类选择器

只有存在URL指向该匹配元素时,样式效果才会生效

1
2
3
4
5
6
7
// 点击按钮时,跳到target位置,target背景为红色
<div id="target">target</div>
<a href="#target" class="btn">View project on GitHbu</a>
...
#target:target {
background: red;
}

语言伪类选择器

需要指定html或bogy的lang属性
<html lang="en-US">或<body lang="fr">
E:lang(language) 表示选择匹配E的所有元素,且匹配元素指定了lang属性,而且其值为language。

UI元素状态伪类选择器

:checked 选中状态,适用复选或单选
:enabled 启用状态,匹配启用的表单元素
:disabled 不可用状态,匹配禁用的表单元素

结构伪类选择器

E:first-child 作为父元素的第一个子元素的元素E,与E:nth-child(1)相同
E:first-child 作为父元素的最后一个子元素的元素E,与E:nth-last-child(1)相同
E:root 选择匹配元素E所在文档的根元素,在HTML文档中,根元素始终是html,此时该选择器与html类型选择器匹配的内容相同
E F:nth-child(n) 选择父元素E的第n个子元素F,n的起始值为1
E F:nth-last-child(n) 选择父元素E的倒数第n个子元素F
E:nth-of-type(n) 选择父元素内具有指定类型的第n个E元素
E:nth-last-of-type(n) 选择父元素具有指定类型的倒数第n个E元素
E:first-of-type 选择父元素内具有指定类型的第一个E元素,与E:nth-of-type(1)相同
E:last-of-type 选择父元素内具有指定类型的最后一个E元素,与E:nth-last-of-type(1)相同
E:only-child 选择父元素只包含一个子元素,且该子元素匹配E元素
E:only-of-type 选择父元素只包含一个同类型的子元素,且该子元素匹配E元素
E:empty 选择没有子元素的元素,且该元素也不包含任何文本节点

否定伪类选择器

:not(footer){} // 选择页面中所有元素,除了”footer”元素

伪元素

双冒号与单冒号在CSS3中主要用来区分伪类和伪元素

::first-letter 用来选择文本块的第一个字母
::first-line 用来选择文本块的第一行
::before 和 ::after 需配合content属性,不是指存在与标记中的内容,而是可以插入额外内容的位置
::selection 用来匹配选中的文本,紧接收background和color属性

属性选择器

E[attr] 选择匹配具有属性attr的E元素
E[attr=val] 选择匹配具有属性attr的E元素,且attr属性值为val
E[attr|=val] 选择匹配E元素,且E元素定义了属性attr,attr属性值是一个具有val或者以val-开始的属性值
E[attr~=val] 选择匹配E元素,且E元素定义了属性attr,attr属性值具有多个空格分隔的值,其中一个值等于val
E[attr*=val] 选择匹配元素E,且E元素定义了属性attr,其属性值任意位置包含了val
E[attr^=val] 选择匹配元素E,且E元素定义了属性attr,其属性值以val开头
E[attr$=val] 选择匹配元素E,且E元素定义了属性attr,其属性值以val结尾

React-测试

发表于 2016-10-15   |   分类于 React

前端自动化单元测试

使用Karma + Mocha + Chai + Sinon + Enzyme测试React + Redux的web应用,还可能用到Jsdom


工具介绍

Karma 测试过程管理工具,最重要的是可以 webpack 预处理

Mocha 测试框架,提供describe、it等函数

Chai 断言库,提供expect等函数

Sinon 是一个mock框架,可以对任何对象进行mock

Enzyme React测试工具,是对React官方测试工具 react-addons-test-utils 的封装

Jsdom 提供DOM环境,即window、document和navigator对象


参考教程

前端自动化单元测试初探– 主要介绍Karma配置

React 测试入门教程– 主要介绍React组件怎么测试

React+Redux单元测试一小时入门、参考代码– 主要介绍组件、action、reduce怎么测试

Karma+Webpack配置

  • 依赖包括
install --save-dev 如下依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
chai
enzyme
isparta-instrumenter-loader
jsdom
karma
karma-chai
karma-chrome-launcher
karma-coverage
karma-mocha
karma-sinon
karma-sinon-chai
karma-webpack
lodash
mocha
null-loader
react-addons-test-utils
sinon
sinon-chai
webpack-node-externals
  • karma.conf.js 文件如下
init 生成karma.conf.js修改后
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
const webpackconfig = require('./webpack.config.js');
module.exports = function(config) {
config.set({
basePath: '',
frameworks: ['mocha','sinon','chai','sinon-chai'],
files: [
'./test/*.js'
],
exclude: [
],
preprocessors: {
'./src/containers/*': ['coverage'],
'./test/*.js': ['webpack']
},
reporters: ['progress', 'coverage'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
concurrency: Infinity,
webpack: webpackconfig
})
}
  • webpack配置文件需要添加如下内容,否则使用enzyme报错
1
2
3
4
5
6
7
8
externals: {
'jsdom': 'window',
'cheerio': 'window',
'react/addons': true,
'react/lib/ExecutionEnvironment': true,
'react/lib/ReactContext': true
}

测试代码样例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import React from 'react';
import { Breadcrumb } from 'antd';
// 测试使用模块
import { shallow , mount, render } from 'enzyme';
// 待测试组件
import NavPath from '../src/components/NavPath';
describe('NavPath Shallow', function () {
it('Component Test', function () {
const breadCrumb = [
{key: '0qqnL2f1J42bV3I9Hzavkgg%|%请假管理', name: '请假管理'},
{key: '0TT3DR6jt6Fogaimc3l3Rr%|%外出请假申请单', name: '外出请假申请单'},
];
const context = {
router: {},
location: {},
};
const navPath = shallow(<NavPath breadCrumb={breadCrumb} />, { context });
expect(navPath.find(Breadcrumb.Item).length).to.equal(2);
});
});

启动测试

1
karma start

CSS-零散知识

发表于 2016-09-28   |   分类于 CSS

text-indent

段落的第一行缩进

css hack

css hack 目的就是使你的CSS代码兼容不同的浏览器

CSS border实现各个方向等腰直角三角

HTML代码:

1
2
3
4
<span class="border_cort"></span>
<span class="border_corb"></span>
<span class="border_corl"></span>
<span class="border_corr"></span>

CSS代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
.border_cort, .border_corr, .border_corb, .border_corl {
display: inline-block;
width: 0;
height: 0;
border-width: 6px;
overflow: hidden;
background-color: red;
}
.border_cort {
border-color: #333 transparent transparent;
border-style: solid dotted dotted;
}
.border_corr {
border-color: transparent #333 transparent transparent;
border-style: dotted solid dotted dotted;
}
.border_corb {
border-color: transparent transparent #333;
border-style: dotted dotted solid;
}
.border_corl {
border-color: transparent transparent transparent #333;
border-style: dotted dotted dotted solid;
}

!important

使用!important定义的样式拥有最高的优先级,IE6不支持,会按正常优先级显示,可以解决一些浏览器的兼容性问题。

两个div显示在一行,分居左右

对两个div元素A、B都添加diaplay:inline-block属性,对居右元素B添加float:right属性,在B元素的后面需添加一个div并有clear:both属性用于清除float。

min-height

由于某些原因导致相同元素不同高度,在IE浏览器下导致页面布局发生变化(有空位),可以给这些相同元素添加min-height属性,取值大于等于这些不同高度中的最大值,即可是页面布局恢复正常。

必填样式

常见的必填样式都是在label前添加一个红色星号,可以在判断该项为必填项时给label添加一个class如notNull,并使用伪类

1
2
3
4
.notNull::before {
color: red;
content: "*";
}

元素居中

top: 50%;
left: 50%;
margin-top: 50px;// 居中元素的高度/2
margin-left: 50px;// 居中元素的宽度/2

img {
display: block;
margin: 0 auto;
}

文字居中

text-align: center; // 水平居中
line-height: @divHeight; // 垂直居中,属性值等于文字所在块的高度

word-break属性

word-break属性规定自动换行的处理方法,通过使用该属性,可以让浏览器实现在任意位置的换行。

normal,使用浏览器默认的换行规则
break-all,允许在单词内换行
keep-all,只能在半角空格或连字符处换行

word-wrap属性

word-wrap属性允许长单词或URL地址换行到下一行。

normal,只在允许的断字点换行(浏览器保持默认处理)
break-word,在长单词或URL地址内部进行换行

图片未加载完成的占位

1
2
3
4
5
6
7
8
9
10
11
12
13
.imgWrapper {
position: relative;
height: 0;
padding-top: 190%; // 图片高度 / 图片宽度 * 100%
overflow: hidden;
}
img {
position: absolute;
top: 0;
width: 100vw;
height: auto;
}

获取滚动条宽度

1
2
3
4
5
6
7
8
9
10
// 设置一个不可见的div,查看设置 scroll 前后的宽度差
function getScrollWidth() {
const oDiv = document.createElement("DIV");
oDiv.style.cssText = "position:absolute;top:-1000px;width:100px;height:100px; overflow:hidden;";
const noScroll = document.body.appendChild(oDiv).clientWidth;
oDiv.style.overflowY = "scroll";
const scroll = oDiv.clientWidth;
document.body.removeChild(oDiv);
return noScroll-scroll;
}

英文单词使用连字符换行

设置标签的 lang="en",css 中添加 hyphens: auto;。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<html lang="en">
<head>
<style>
p {
width: 80px;
border: 1px solid black;
hyphens: auto;
}
</style>
</head>
<body>
<p lang="en" class="auto">An extremely long English word</p>
</body>
</html>

CSS-布局基础2

发表于 2016-09-21   |   分类于 CSS

长度单位

绝对长度单位

in(英寸)、cm(厘米)、mm(毫米)、pt(磅)、pc(pica)

相对长度单位

  • px 像素
  • em 是相对于文字大小的值,实际大小是受到字体尺寸影响的。例如:定义某个元素的文字大小为12pt,那么,对于这个元素来说1em就是12pt。
  • ex 和em类似,指的是相对于文本中字母x的高度,因为不同的字体的x的高度是不同的,所以ex的实际大小,受到字体和字体尺寸两个因素的影响。
  • % 相对于包含块的计量单位
  • vw 基于视窗宽度的大小,10vw表示视窗宽度的10%
  • vh 基于视窗高度的大小,10vh表示视窗高度的10%

替换元素和非替换元素

替换元素

替换元素是浏览器根据其标签的元素与属性来判断显示具体的内容,如<img>、<input>、<textarea>、<select>、<object>

非替换元素

大多数元素是不可替换元素,他们将内容直接告诉浏览器,将其显示出来,如<p>

块级元素和行内元素

块级元素

最明显的特征就是独自占领一行,自动充满父级元素的内容区域,如div、p、form、header、footer、section
通过CSS设定了浮动(float)以及设定显示(display)属性为’block’或’list-item’的元素都是块级元素

行内元素

允许左右都可以有元素,最常见的就是
通过display:’inline’设置以后都会变成行内元素

浏览器的标准模式和怪异模式

  • 标准模式(strict mode):浏览器按W3C标准解析执行代码
  • 怪异模式(quirks mode):使用浏览器自己的方式解析执行代码

    浏览器解析时到底使用标准模式还是怪异模式,与网页中的DTD声明直接相关,DTD声明定义了标准文档的类型(标准模式解析)文档类型,会使浏览器使用相应的方式加载网页并显示,忽略DTD声明,将使网页进入怪异模式(quirks mode)。

怪异模式

1
2
3
4
5
6
7
8
<html>
<head>
<title>重庆PHP</title>
</head>
<body>
<h3>重庆PHP,最专业的PHP社区</h3>
</body>
</html>

标准模式

1
2
3
4
5
6
7
8
9
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>重庆PHP</title>
</head>
<body>
<h3>重庆PHP,最专业的PHP社区</h3>
</body>
</html>

margin属性

1
2
3
4
margin:2cm 4cm 3cm 4cm;// 值分别代表上外边距、右外边距、下外边距、左外边距
margin:10px 5px 15px;// 值分别代表上外边距、右外和左外边距、下外边距
margin:10px 5px;// 值分别代表上外和下外边距、右外和左外边距
margin:10px;// 值代表四个外边据

可选的属性值如下:

auto: 浏览器计算外边距
length: 具体单位计的外边距值,比如px、cm等,默认值是 0px
%: 基于父元素的宽度的百分比
inherit: 从父元素继承

说明如下:

  • 块级元素的垂直相邻外边距会合并,由于块级元素独占一行,不考虑水平外边距
  • 行内元素实际上不占上下外边距,左右外边距不会合并
  • 浮动元素的外边距也不会合并
  • 允许指定负的外边距值,不过使用时要小心

设置块级元素的 width 可以阻止它从左到右撑满容器,再设置左右外边距为 auto 来使其水平居中。但是,当浏览器窗口比元素的宽度还要窄时,浏览器会显示一个水平滚动条来容纳页面。此时可以使用 max-width 替代 width 可以使浏览器更好地处理小窗口的情况。

float属性

可选的属性值如下:

left: 元素向左浮动
right: 元素向右浮动
none(默认): 元素不浮动,并会显示在其在文本中出现的位置
inherit: 从父元素继承

说明如下:

  • 用来定义元素在哪个方向浮动,浮动元素会生成一个块级框,而不论它本身是何种元素
  • 这个属性总应用于图像,使文本围绕在图像周围
  • 如果浮动非替换元素,如div,则要指定一个明确的宽度;否则,它们会按元素内容尽可能地窄
  • 如果行内有极少的空间可供浮动元素,那么这个元素会跳至下一行,直到某一行拥有足够的空间为止
  • 如果浮动元素超出父容器,在父容器添加overflow:auto即可根据浮动的元素撑开容器
  • clear属性可清除浮动,值可为left或right,分别表示清除向左浮动或向右浮动
  • float只是脱离了文档流的dom空间但是还占据着文字空间

盒模型

W3C标准盒模型

1
2
3
4
5
6
外盒尺寸计算(元素空间尺寸)
element 空间高度 = 内容高度 + 内距 + 边框 + 外距
element 空间宽度 = 内容宽度 + 内距 + 边框 + 外距
内盒尺寸计算(元素大小)
element 高度 = 内容高度 + 内距 + 边框 (height为内容高度)
element 宽度 = 内容宽度 + 内距 + 边框 (width为内容宽度)

IE传统盒模型(IE6以下)

1
2
3
4
5
6
外盒尺寸计算(元素空间尺寸)
element 空间高度 = 内容高度 + 外距(height包含了元素内容高度、边框、内距)
element 空间宽度 = 内容宽度 + 外距(height包含了元素内容高度、边框、内距)
内盒尺寸计算(元素大小)
element 高度 = 内容高度(height包含了元素内容高度、边框、内距)
element 宽度 = 内容宽度(width包含了元素内容宽度、边框、内距)

边框和内距都包含在宽度或高度内!!!

box-sizing属性

可选的属性值如下:

content-box(默认): 维持W3C的标准盒模型
border-box: 维持IE的传统盒模型
padding-box(Firefox浏览器): 指定元素的宽度或高度包含内容的宽度或高度和内距
inherit: 从父元素继承

说明如下:

  • 定义盒模型的尺寸解析方式,主要目的是控制元素的总宽度

outline属性

  • 外轮廓outline的效果和边框border的效果极其相似,不同之处在于outline不占用网页布局空间,不一定是矩形,属于一种动态样式,可以向内扩展。

  • 将outline属性设置在:focus上,可以实现在元素获取到焦点或者被激活时呈现边框的效果。

  • 为了给元素添加边框效果又不影响页面布局,可以使用outline属性。

position属性

可选的属性值如下:

static(默认): 元素不会被特殊的定位,top, right, bottom, left 和 z-index 属性无效。
relative: 相对定位,相对于其在普通流中的正常位置进行定位,元素仍保持其未定位前的形状,它原本所占的空间仍保留。
absolute: 绝对定位,不为元素预留空间。元素相对于最近的非static定位的祖先元素来定位。
fixed: 固定定位,不为元素预留空间。元素会相对于视窗来来定位,当出现滚动条时,对象不会随着滚动。
inherit: 从父元素继承。

说明如下:

position: sticky,类似relative和fixed的合体,当目标区域在屏幕中可见时,它的行为就像relative; 当页面滚动超出目标区域时,它的行为就像fixed,固定在目标位置。目前支持该属性的浏览器比较少。

top、right、bottom、left 属性

top、right、bottom、left 分别定义了定位元素上、右、下、左外边距边界与其包含块右边界之间的偏移。可选的取值如下:

auto(默认): 通过浏览器计算的位置
%: 设置以包含元素的百分比计的位置,可使用负值
length: 使用 px、cm 等单位设置元素的位置,可使用负值
inherit: 规定应该从父元素继承属性的值

说明如下:

对于 static 元素,为 auto。对于相对定义元素,如果 top 和 bottom 都是 auto,其计算值则都是 0;如果其中之一为 auto,则取另一个值的相反数;如果二者都不是 auto,bottom 将取 top 值的相反数;当 left 和 right​ ​​​​​同时指定时,如果容器是从左到右时,left的值会被优先设定,如果容器是从右到左时,right的值会被优先设定。

overflow 属性

可选的属性值如下:

visible(默认): 内容不会被修剪,会呈现在元素框之外
hidden: 内容会被修剪,并且其余内容是不可见的
scroll: 内容会被修剪,但是浏览器会显示滚动条以便查看其余的内容
auto: 如果内容被修剪,则浏览器会显示滚动条以便查看其余的内容
inherit: 从父元素继承

说明如下:

如果值为 scroll,不论是否需要,用户代理都会提供一种滚动机制。因此,有可能即使元素框中可以放下所有内容也会出现滚动条。

clip 属性

用于裁剪绝对定位元素,可选的属性值如下:

auto(默认): 不应用任何剪裁
shape: 设置元素的形状,唯一合法的形状值是:rect(top, right, bottom, left)
inherit: 从父元素继承

z-index 属性

设置元素的堆叠顺序,仅能在定位元素上奏效,即 position 为 relative 或 absolute 的元素上。

vertical-align 属性

可选的属性值如下:

baseline(默认): 元素放置在父元素的基线上
sub: 垂直对齐文本的下标
super: 垂直对齐文本的上标
top: 把元素的顶端与行中最高元素的顶端对齐
text-top: 把元素的顶端与父元素字体的顶端对齐
middle: 把此元素放置在父元素的中部
bottom: 把元素的底端与行中最低的元素的底端对齐
text-bottom: 把元素的底端与父元素字体的底端对齐
lenght: 用长度值指定由基线算起的偏移量,允许使用负值
%: 使用 “line-height” 属性的百分比值来排列此元素,允许使用负值
inherit: 从父元素继承

说明如下:

vertical-align 影响 inline-level 元素,这些元素的 display 属性为 inline、inline-block、inline-table,基本的inline元素都是标签裹着文字。

inline 的元素在一行中一个挨着一个,当这些元素超出了他们的所在行,一个新行便会建立在它下方。这里的每一行就叫做line box。每一行不同尺寸的元素意味着 line box 不同的高度。在这些 line boxes 里面 vertical-align 属性负责摆放单独的元素。

零散知识

发表于 2016-09-21   |   分类于 Other

window.parent和window.opener区别

  • window.parent能获取一个框架的父窗口或父框架。顶层窗口的parent引用的是它本身。
  • window.opener引用的是window.open打开的页面的父页面。

解决不支持html5标签的办法

方式一:自己创建标签节点

1
2
3
<script>
document.createElement(e[i]);
</script>

方式二:使用Google的html5shiv包(推荐)
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>

不管使用以上哪种方法,都要初始化新标签的CSS

1
2
3
article,aside,dialog,footer,header,section,footer,nav,figure,menu{
display: block;
}

方式三:防弹衣技术
Bulletproof技术,防弹衣技术,建议在所有新的HTML5块级元素中增加一个内部的div元素,然后包含一个CSS class,用这个元素来替代HTML元素。

1
2
3
4
5
6
7
8
9
<section>
  <div class="section">
  <!-- content -->
  </div>
</section>
.section {
     color: blue;
 }

outerHTML、innerText、innerHTML

echo

cmd中输入echo %PATH% 可查看path环境变量

Chrome浏览器用户名、密码回填问题

前几天发现一个奇怪的问题,使用Chrome浏览器在登录输入用户名、密码后,点击了保存用户名密码,在其它页面发生了用户名、密码的回填。

解决方案:在使用到Input的地方,都添加了autoComplete属性值为off和name属性,但是不同的name属性值,有的可能不会回填有的可能就会回填,我有点怀疑人生了。

而且对于没有回填用户信息的情况,在点击密码框的时候,还是会有用户密码信息的提示,这个还没有找到解决方案。

端口占用

windows

netstat -aon | findstr 8088 # 查看指定端口被哪个进程占用,会返回一个进程号
tasklist | findstr 6808 # 查看指定进程号是哪个程序的
taskkill -F -IM javaw.exe # 杀死应用程序

mac

sudo lsof -i :4000
sudo kill -9 [PID]

eclipse分配内存

右键项目 -> Run As -> -Run Configurations -> Arguments, 添加下面的信息到VM arguments中

1
2
-Xms256m
-Xmx512m -XX:MaxNewSize=256m -XX:MaxPermSize=512m

Xshell指令更新环境

停 tomcat 重新启动

1
2
3
4
5
6
7
ps -ef|grep java # 查进程号,12997
kill -9 12997 # 杀死进程
cd /usr/local/tomcat6/bin/ # 进入到目录
./catalina.sh start # 启动
cd ..
cd logs
tail -f catalina.out # 打日志

Chrome插件生成crx文件

扩展程序->开发者模式->打包扩展程序->根据扩展程序ID,如hgmloofddffdnphfgcellkdfbfbjeloo,在C:\Users\Administrator\AppData\Local\Google\Chrome\User Data\Default\Extensions\路径下找到指定ID(hgmloofddffdnphfgcellkdfbfbjeloo)文件,双击进入,选择文件夹->打包扩展程序,即可生成crx文件。

Thunk函数的含义

编译器的”传名调用”实现,往往是将参数放到一个临时函数之中,再将这个临时函数传入函数体。这个临时函数就叫做Thunk函数。

1
2
3
4
5
6
7
8
9
10
11
12
function f(m){
return m * 2;
}
f(x + 5);
// 等同于
var thunk = function () {
return x + 5;
};
function f(thunk){
return thunk() * 2;
}

在JavaScript语言中,Thunk函数替换的不是表达式,而是多参数函数,将其替换成单参数的版本,且只接受回调函数作为参数。

1
2
3
4
5
6
7
8
9
10
11
// 正常版本的readFile(多参数版本)
fs.readFile(fileName, callback);
// Thunk版本的readFile(单参数版本)
var Thunk = function (fileName){
return function (callback){
return fs.readFile(fileName, callback);
};
};
var readFileThunk = Thunk(fileName);
readFileThunk(callback);

数据库表回滚

1
2
3
# 表sy_org_user1回滚到2015-01-09 15:00:00时间
alter table sy_org_user1 enable row movement;
flashback table sy_org_user1 to timestamp to_timestamp('2015-01-09 15:00:00','yyyy-mm-dd hh24:mi:ss');

Oracle数据库导入导出

1
2
3
4
5
6
7
8
9
# 导出库
exp DEV_BASE/DEV_BASE@pcmdb_192.168.100.30 file=E:\DEV_BASE.dmp owner=DEV_BASE
# 如果以前导入过,删除用户再重建
drop user DEV_BASE cascade;
create user DEV_BASE identified by DEV_BASE;
grant resource, connect, dba to DEV_BASE;
alter user DEV_BASE quota unlimited on PCMDB;
# 导入库
imp system/sys1234@orcl fromuser=DEV_BASE touser=DEV_BASE file=E:\DEV_BASE.dmp

响应式编程

响应式编程是一种面向数据流和变化传播的编程范式,这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。响应式编程最初是为了简化交互式用户界面的创建和实时系统动画的绘制而提出来的一种方法,但它本质上是一种通用的编程范式。在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
<!--在工程入口html文件的body标签中添加如下script内容-->
<body id="body">
<div id="react-content">
<script>
function cheackBowser() {
var userAgent = navigator.userAgent; //取得浏览器的userAgent字符串
var version = navigator.appVersion; // 浏览器版本信息
var isOpera = userAgent.indexOf("Opera") > -1; //判断是否Opera浏览器
var isChrome = userAgent.indexOf("Chrome") > -1; //判断是否Chrome浏览器
var isIE = userAgent.indexOf("compatible") > -1 && userAgent.indexOf("MSIE") > -1 && !isOpera; //判断是否IE浏览器
var isFF = userAgent.indexOf("Firefox") > -1; //判断是否Firefox浏览器
var isSafari = userAgent.indexOf("Safari") > -1; //判断是否Safari浏览器
//如果是 IE 火狐 opera,提示下载qq浏览器
if(isIE || isFF || isOpera) {
//主页面不显示只显示弹出框,弹出弹框提示下载qq浏览器
if(confirm("您使用的浏览器存在兼容问题,为了您更好的用户体验,建议您点击确定下载QQ浏览器")) {
document.getElementById("react-content").style.display = "none";
window.open("http://browser.qq.com/?adtag=SEM1", "_self");
} else {
//点击取消关闭此页面
window.close();
//如果关闭此页面被阻止则把页面清空
if(window){
window.location.href="about:blank";
}
}
} else if(isChrome){
var detailVersion = Number(VersionFilter(version)[0].substring(7,9));
if(detailVersion < 45) {
document.getElementById("react-content").style.display = "none";
if(confirm("您使用的浏览器版本太低,为了您更好的用户体验,建议您点击确定下载QQ浏览器")) {
window.open("https://dldir1.qq.com/invc/tt/QQBrowser_Setup_SEM1.exe","_self");
}
} else {
//谷歌浏览器,版本符合要求
}
}
}
// 获取详细的chrome版本
function VersionFilter (value) {
var reg = /Chrome\/\d{2}\.\d+\.\d+/
return reg.exec(value)
}
cheackBowser()
</script>
</div>
</body>

打版本号

在做项目时,测试环境只要更新,最好打个版本号(tag),这样如果这次测试的结果比较稳定,要向线上更新,直接更新指定tag的代码到线上就行了,开发人员还是正常开发提交代码。

元编程

简单的理解可以说成是“关于编程的编程”,或者说“与编程相关的编程”,如程序甲可以输出 A ~ Z,而程序乙可以生成程序甲,那么编写程序乙的活动就可以算作元编程。元编程更狭义的意思应该是指“编写能改变语言语法特性或者运行时特性的程序”,一种语言本来做不到的事情,通过编程来修改它,使得它可以做到了,这就是元编程。

robots.txt

【百度百科】robots协议也叫robots.txt(统一小写)是一种存放于网站根目录下的ASCII编码的文本文件,它通常告诉网络搜索引擎的漫游器(又称网络蜘蛛),此网站中的哪些内容是不应被搜索引擎的漫游器获取的,哪些是可以被漫游器获取的。因为一些系统中的URL是大小写敏感的,所以robots.txt的文件名应统一为小写。robots.txt应放置于网站的根目录下。如果想单独定义搜索引擎的漫游器访问子目录时的行为,那么可以将自定的设置合并到根目录下的robots.txt,或者使用robots元数据(Metadata,又称元数据)。

1…151617
© 2021 小朱
由 Hexo 强力驱动
主题 - NexT.Pisces