前端0:js,css基础
1.js是按顺序执行的,故把 <script> 内容放到 <head> 里,或放到 <body> 内容之前,与<script> 内容放到 <body> 内容之后 执行的效果会有所不同。经常会遇到:
①<head><script> 里修改图片路径 obj.src 失败,由于未加载 window.onload。
2.节点处理
由于浏览器兼容性差异,各标签之间的空白也算为节点,index 随之变化。
3.查询字符串:JS与服务器的通信,避免URL等数据格式不正确即未经编码,导致通信错误。
1 function addQueryStringArg(url,name,value){ 2 if(url.index("?")==-1){ 3 url+="?"; 4 }else{ 5 url+="&"; 6 } 7 url+=encodeURLComponent(name)+"="+encodeURLComponent(value); 8 return url; 9 }
4.数组处理:数组其实和对象一样,也是有属性的,即 0,1,2,3...,所以才能用下标查找,同时可以用原型链查找属性的方法 key in array。
arr=null 数组删除
arr.pop 去尾,返回尾巴
arr.join('.') 数组转字符串,以 . 分开,一般配合 split
arr.reduce(fn) 接收一个函数进行两两迭代,数组中的每个值(从左到右)开始合并,最终计算为一个值。
arr.map(fn,cb) 参数为函数表达式及回调,fn(a,b,c) 参数默认为当前数组值,当前数组值索引,etc
arr.shift() 删除头,返回头
arr.unshift('a') 加头,返回length
arr.push 加尾,返回length
arr.splice(index,howmany,...itemn) 添加删除,返回原arr,index表示操作位置,index初始为0;howmany表示删除个数,howmany=0为不删除,改插入,插入index-1位置;...itemn为加入的参数若干,非纯函数,输出改变
var xs = [1,2,3,4,5];
// 不纯的
xs.splice(0,3);
//=> [1,2,3]
xs.splice(0,3);
//=> [4,5]
xs.splice(0,3);
//=> []
纯函数:arr.slice(start,end) 提取,返回子arr,内容是从 start 处到 end-1,参数可负,原arr不变,纯函数输出不变
Array.prototype.slice.apply(arguments) 类数组转数组 e.g.call
arr.indexOf
arr.find(callback) 查找满足条件的第一个值
arr.findIndex(callback)
字符串处理:
str.substring(start,end) 提取,返回子str,内容是从 start 处到 end-1,if (start<0) start=0,原str不变
str.split() 分割,返回数组arr,若需要进一步split,注意:分割后为数组,故用for循环split
str.parse() 转成json,注意:字符串中的属性要严格加上引号
str.substr(start,length) 返回子str,注意:不建议使用
字符串的部分修改可以使用replace 或者拼接
str.slice
str.indexOf
str.replace 正则替换
5.underscore对象处理:
underscore 待整理
if ( 'a' in obj ) 判断对象 (json)obj 中是否有 a,注意 'a' 的 '',同样 obj 可以替换为 arr,但此时只能寻找下标 0,1,2...
json处理:
json.stringify() 转成str
json 转 url string:for in 遍历,push( = ) 存入数组,join('&') 转成 str,补套接字
json.parse() 将字符串转换成 json
遍历用 for in 后存入 push 数组
7.switch(true){case 80<n:document.write('优秀');break;}//当表达式为比较时,参数为布尔值。
8.Math.floor/Math.ceil/Math.round//要上下兼顾建议floor,使用Math.floor(Math.random()*(max-min+1)+min)
9.页面所获取的内容都是字符串,eg:typeof getElementById(ID).value
10.URL-encode处理避免读取错误请求 url: encodeURLComponent()
e.g. encodeURLComponent("MTAuMTA4LjUyLjM2O2FkbWluO2ExMjM0NTY3OzgwMDA=") //MTAuMTA4LjUyLjM2O2FkbWluO2ExMjM0NTY3OzgwMDA%3D
11.<button onclick='func(event)'></button>//event必须通过实际参数形式传递给函数func(e),考虑是否需要全局window
{alert(e.target.value);}
12.javascript 是单线程的,但浏览器内部不是单线程的。你的一些 I/O 操作,定时器的计时和事件监听(click,keydown...)等都是由浏览器提供的其他线程来完成的。
改变 this 指向:将 this 添加到指定环境中,若环境为 null,则指向 window。
.call(环境,参数1,参数2)
.apply(环境,[参数1,参数2])
.bind(环境,参数1,参数2...)
总结:call和apply都是改变上下文中的this并立即执行这个函数,bind方法可以让对应的函数想什么时候调就什么时候调用,并且可以将参数在执行的时候添加。
e.g.
var a = {
user:"追梦子",
fn:function(e,d,f){
console.log(this.user); //追梦子
console.log(e,d,f); //10 1 2
}
}
var b = a.fn;
var c = b.bind(a,10); //或者 var d = b.bind(a,10,1,2); 或者 var e = b.bind(a)
c(1,2);
13.动态创建的dom元素
在此基础上添加监听事件,需要全局方法 <dom onclick='window.fn()' /> 或查询相关资料。
14.禁止使用 遍历节点添加事件绑定
解决方法:父级监听事件委托/代理 addEventListener('events', fn, false);
15.try catch 异步编程中
try 代码块中避免使用赋值逻辑,并结合类型检查 typeof
朴灵曾提到:异步编程的难点之一是异常处理,书中描述 "尝试对异步方法进行try/catch操作只能捕获当次事件循环内的异常,对call back执行时抛出的异常将无能为力"。
16.闭包的属性方法
var arr = []; for(var i = 0; i < 2; i++){ (arr[i] = function self(){ console.log(self.x); }).x = i; }; arr[1](); // 1
17.判断变量的类型
Object.prototype.toString.call(1) //"[object Number]"
18.apply, call 不过是改变函数上下文
let obj = {} fn(a, b) fn.apply(obj, arr) // 解析: // obj 为加入的对象,该参数表示需要变更的执行大小文,说白了,就是改变原本fn的指向,即this。 // arr:Array 即传递给fn的参数,['A', 'B'] 对应 fn的arguments fn.call(obj, 'A','B')
所以当你需要某个函数中的属性和方法,只需要在:目标函数里 { 需要的函数.call(this) } 即可。
fn.call() // or apply 会使函数立即执行
bind 与 call 有点类似,不同点在于
var func1 = fn.bind(obj); func1(); // 解析:绑定obj后不会立即执行函数,而是返回一个改变了上下文 this 后的新函数。原函数fn的this依旧为window
// call 是把第二个及以后的参数作为 func 方法的实参传进去,而 func1 方法的实参实则是在 bind 中参数的基础上再往后排
自己实现bind
if (!Function.prototype.bind) { Function.prototype.bind = function () { var self = this, // 保存原函数 context = [].shift.call(arguments), // 保存需要绑定的this上下文 args = [].slice.call(arguments); // 剩余的参数转为数组 return function () { // 返回一个新函数 self.apply(context,[].concat.call(args, [].slice.call(arguments))); } } }
apply, call, bind 请参考 https://github.com/lin-xin/blog/issues/7
apply, call, bind 涉及的闭包:与 js 执行上下文相关,遇到 执行 函数 (...实参) ,即创建一个新的执行上下文。同时 js 引擎将该上下文添加到调用堆栈。当调用结束后从调用堆栈中删除。
19.元素置换[乱起的名]
let arr = [1, 3, 2]; let val = arr[0]; arr[0] = arr [1]; arr[1] = val console.log(arr); // [3, 1, 2] // 引申快捷操作: // 外面数组表示对应位置互相置换 // 同时改变里面成员的值 [arr[0], arr[1]] = [arr[1], arr[0]]
20.深拷贝:完全拷贝一个新对象,修改时原对象不再受到任何影响。 JSON.parse(JSON.stringify(obj))
21.fs.readFile 读文件结果为buffer,buffer.toString()
22.让代码继续下去
try{ // 捕获错误 }catch(e){ // 不放内容,直接运行下去 } // 或者reject catch await Promise.reject('出错内容').catch(err => { console.log(err) })
实例:确保每个异步都能正常进行下去
async function fn() { try{ let f1 = await readFile('data/a.txt') let f2 = await readFile('data/b.txt') }catch(e){} console.log(f1.toString()) console.log(f2.toString()) }
23.类静态属性
class Test {
static fn () {}
}
Test.fn()
24.import * as name
语法导入所有导出接口,即导入模块整体
25.text-align: justify; // 文字两端对齐
26.for of 解决 forEach 无法 return 结束的问题
for (let [idx, val] of delayList.entries()) { }
27.异步生成数组,同步直接在for循环里处理逻辑即可
(function next(i, len, callback) { if (i < len) { async(arr[i], function (value) { arr[i] = value next(i+1, len, callback) }) } else { callback() } } (0, arr.length, function () { // all array items have processed }))
28.同步执行,并根据最终数组进行后续处理
(function next2(i, len, count, callback) { for (; i < len; i++) { (function (i) { async(arr[i], function (value) { arr[i] = value if (++count === len) { callback() } }) }(i)) } } (0, arr.length, 0 ,function () {}))
29.事件委托(兼容)
let oUl = document.querySelector('ul-class') oUl.onmouseover = function (e) { var e = e || window.event; var oLi = e.srcElement || e.target; if (oLi.nodeName.toLowerCase() === 'li') { oLi.style.background = 'red'; } }
30.async 与 promise.all
async function A1() { let res = await Promise.all([cPromise, cPromise]); // 简单但灵活性降低了,只有都成功或者都失败两种情况。 console.log(res) } // 推荐 async function A2() { let res = []; let reqs = [cPromise(), cPromise()]; // 注意这里其实已经开始执行异步; for (let i=0; i<reqs; i++) { // 基本没有消耗 res[i] = await reqs[i] } console.log(res) }
async 的错误处理
function createPromise (needCatch:boolean) { let p = new Promise((resolve, reject) => { reject(10); }); return needCatch ? p.catch(err => err) : p; // 提前包装捕获错误或者在 async 函数中使用 try.catch }
31.使用 Boolean 构造函数过滤数组中的所有假值
const compact = arr => arr.filter(Boolean); compact([0, 1, false, 2, '', 3, 'a', 'e'*23, NaN, 's', 34]); // [1, 2, 3, 'a', 's', 34]
32.取整 | 0
33.判断奇偶 !!(num & 1); 奇数为true, 偶数为 false
34.强制参数
mandatory = () => { throw new Error('Missing parameter !'); } foo = (bar = mandatory()) => { return bar; }
35.惰性载入函数,首次判断后覆写原函数,之后结果统一用复写的
function foo() { a !==b ? console.log('aaa') : console.log('bbb') } // 覆写后,当判断分支足够多时,节约的资源还是很客观的 function newFoo () { if (a !== b) { foo = () => console.log('aaa') } else { foo = () => console.log('bbb') } return foo(); }
36.一次性函数
var sca = function () { console.log('msg') // 只执行一次,之后重新覆写 sca = function () { console.log('foo') } }
37.字符串比较时间先后:字符串是从左到右比较每个字符的charCode的,注意:时间形式需要补0;
console.log('2014-08-08' < '2014-09-09'); // true console.log('21:00' < '09:10') // false
// 数字补0
const addZero = (num, len = 2) => (`0${num}`).slice(-len)
38.精确到指定位数的小数(四舍五入)
const round = (n, decimals = 0) => Number(`${Math.round(`${n}e${decimals}`)}e-${decimals}`) round(1.235, 2) // 1.35 round(1.345, 1) // 1.3
39.统计数组中相同项的个数
var cars = ['BMW', 'Benz', 'Benz', 'Tesla']; var carsObj = cars.reduce(function (obj, name) { obj[name] = obj[name] ? ++ obj[name] : 1; return obj }, {}); carsObj;
40.将数组平铺到指定深度
const flatten = (arr, depth = 1) { depth !==1 ? arr.reduce((a, v) => a.concat(Array.isArray(v) ? flatten(v, depth -1) : v), []) : arr.reduce((a, v) => a.concat(v), []) } flatten([1, [2], 3, 4]); // [1, 2, 3, 4] flatten([1, [2, [3, [4, 5], 6], 7], 8], 2); // [1, 2, 3, [4, 5], 6, 7, 8]
41.数组的对象解构,可以方便地获取数组的第 n 个值
const csvFileLine = '1997,John Doe,US,john@doc.com,New York'; const {2:country, 4:state} = csvFileLine.split(','); // country US // state New York
42.采用解构删除不必要的属性
let {_internal, tooBig, ...cleanObject} = {el1: '1', _internal: 'secret', tooBig: {}, el2: '2', el3: '3'}; console.log(cleanObject); // {el1: '1', el2: '2', el3: '3'}
43.在函数参数中解构嵌套对象
var car = { model: 'bmw 2018', engine: { v6: true, vin: 12345 } } const modelAndVin = ({model, engine: {vin}}) => { console.log(`model: ${model} vin: ${vin}`) } modelAndVin(car)
44.通用表单验证
const schema = { first: {required: true}, last: {required: true} } const validate = (schema, values) => { for (field in schema) { if (schema[field].required) { if (!values[field]) return false } } return true } console.log(validate(schema, {firstL 'Bruce'})); // false console.log(validate, {first: 'Bruce', last: 'Wayne'}); // true
45.两端对齐,数字字母不分割
.text { text-align: justify; word-break: normal; }
46.sort((a, b) => a-b) 为什么是升序,可以简单将 a,b 理解为 unicode,b 比 a 大,a - b 为升序
47.animation 不按预期停止动画,使用 forwards 解决,意为动画到 100 % 就停止,而不恢复到初始
.fr { animation: name 2s ease-in forwards }
48. 静态导入导出
export const a = 1 export const b = 2 const c = 3 export default c import c, {a, b} from __dirname import * as cur from __dirname // { a:1, b: 2, default: 3 }
49.web worker 只能加载网络同源 url 或者字符串 js
class webWorker { /** * @description: webworker的简单封装 * @param {String} data js的url/script的id或class * @param {Object} type 默认值给出,同页面的web worker传个’worker‘即可 * @return: WebWorker 对象 */ constructor(data, type = "url") { this.worker = null; this.workerInit(data, type); } workerInit(data, type) { if (type === "url") { // 默认是以url脚本形式的worker线程 // 此时的data应该是一个url链接 this.worker = new Worker(data); } else { // 以字符串形式创建worker线程,把代码字符串,转成二进制对象,生成 URL,加载URL const blob = new Blob([data]); const url = window.URL.createObjectURL(blob); this.worker = new Worker(url); // 加载 } } /** * @description: 给worker线程发送消息 * @param {*} data 要发送的数据 */ postMessage(data) { return this.worker.postMessage(data); } /** * @description: worker线程发送给主进程的数据 * @param {Function} fn 把数据通过回调的形式传出去 */ onmessage(fn) { this.worker.onmessage = msg => { return fn(msg.data); }; } // 主线程关闭worker线程 closeWorker() { return this.worker.terminate(); } /** * @description: 主线程监听worker线程的错误信息 * @param {Function} fn 错误信息回调 */ errMsg(fn) { this.worker.onerror = e => { return fn(e); }; } } const data = ` // worker线程加载脚本 TODO: Worker 线程无法读取本地文件,加载的脚本必须来自网络 // importScripts('hello1.js', 'http~.js'); // 监听主线程传过来的信息 self.onmessage = e => { console.log('主线程传来的信息:', e.data); // do something }; // 发送信息给主线程 self.postMessage('来自worker线程'); // 关闭worker线程 function closeSon() { return self.close(); }`; const worker = new webWorker(data, "script"); worker.onmessage(data => { console.log("父进程接收的数据:", data); }); worker.postMessage("主进程传给worker线程"); worker.errMsg(msg => { console.log("worker线程报错:", msg); });