前端随笔

205 阅读15分钟

HTML篇

DOM的本质是什么:就是用JS表示的UI元素

DOM和虚拟DOM的区别

  • DOM是由浏览器中的JS提供功能,所以我们只能人为的使用 浏览器提供的固定的API来操作DOM对象;

  • 虚拟DOM:并不是由浏览器提供的,而是我们程序员手动模拟实现的,类似于浏览器中的DOM,但是有着本质的区别;目的是为了实现视图的高效更新

css篇

less和Sass

相同点:

都为css的拓展语言

1、混入(Mixins)——class中的class;

2、参数混入——可以传递参数的class,就像函数一样;

3、嵌套规则——Class中嵌套class,从而减少重复的代码;

4、运算——CSS中用上数学;

5、颜色功能——可以编辑颜色;

6、名字空间(namespace)——分组样式,从而可以被调用;

7、作用域——局部修改样式;

8、JavaScript 赋值——在CSS中使用JavaScript表达式赋值。

不同点:

less是通过Javascript编译,sass是通过ruby编译的,如果没有引入前端工程化,less会消耗客户端性能,sass会消耗服务端性能,但是引入前端工程化的话,gunt,gulp,webpack等,less和sass在打包阶段都会转化成css,所以不会有区别,只是sass是基于ruby,所以每次npm的时候相对慢一点点。

SASS技术的文件的后缀名有两种形式:.sass和.scss。其实两者都是同一种东西,两种均可以可以通过编译生成浏览器能识别的css文件。这两种的区别:

1)扩展名不同;

2)SCSS 的语法书写和CSS 语法书写方式非常类似,.sass文件对代码的排版有着非常严格的要求,而且没有大括号,没有分号;

JavaScript篇

js全局属性/函数

全局属性:

Infinity

代表正的无穷大的数值。

NaN

指示某个值是不是数字值。

undefined

指示未定义的值。

全局函数:

decodeURI()

解码某个编码的 URI。

decodeURIComponent()

解码一个编码的 URI 组件。

encodeURI()

把字符串编码为 URI。

encodeURIComponent()

把字符串编码为 URI 组件。

escape()

对字符串进行编码。

eval()

计算 JavaScript 字符串,并把它作为脚本代码来执行。

isFinite()

检查某个值是否为有穷大的数。

isNaN()

检查某个值是否是非数字值。

Number()

把对象的值转换为数字。

parseFloat()

解析一个字符串并返回一个浮点数。

parseInt()

解析一个字符串并返回一个整数。

String()

把对象的值转换为字符串。

unescape()

对由 escape() 编码的字符串进行解码。

js数组

可以改变原数组的方法:

Array.copyWithin(target[, start[, end])--浅复制数组的一部分到同一数组中的另一个位置,并返回它,不会改变原数组的长度。
Array.fill(value[, start[, end]])--用一个固定值填充一个数组中从起始索引到终止索引内的全部元素
Array.pop()---删除数组的最后一个元素并返回删除的元素。
Array.push()---向数组的末尾添加一个或更多元素,并返回新的长度。
Array.reverse()---反转数组的元素顺序。
Array.shift()---删除并返回数组的第一个元素。
Array.unshift()---向数组的开头添加一个或更多元素,并返回新的长度。
Array.sort()---对数组的元素进行排序。
Array.splice()---用于插入、删除或替换数组的元素。

不会改变原数组的方法:

Array.concat()---连接两个或更多的数组,并返回结果。
Array.entries()---返回一个新的Array Iterator对象,该对象包含数组中每个索引的键/值对
Array.keys()---返回一个包含数组中每个索引键的Array Iterator对象。
Array.values()---返回一个新的 Array Iterator 对象,该对象包含数组每个索引的值
Array.every()---检测数组元素的每个元素是否都符合条件。
Array.some()---检测数组元素中是否有元素符合指定条件。
Array.filter()---检测数组元素,并返回符合条件所有元素的数组。
Array.find()---返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined
Array.findIndex()---返回数组中满足提供的测试函数的第一个元素的索引。若没有找到对应元素则返回-1。
Array.flat(Inifity ?) -- 用于数组降维,不传参默认降一个维度,Inifity 降成一维数组,eg:[1,2,[3,4,[5,6]],].flat(Infinity)  // [1, 2, 3, 4, 5, 6]
Array.flatMap()---首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。它与 map 连着深度值为1的 flat 几乎相同,但 flatMap 通常在合并成一种方法的效率稍微高一些。
Array.includes()---用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回false。Array.indexOf()---搜索数组中的元素,并返回它所在的位置。
Array.lastIndexOf()---返回一个指定的字符串值最后出现的位置,在一个字符串中的指定位置从后向前搜索。Array.join()---把数组的所有元素放入一个字符串。
Array.reduce()---对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。
Array.reduceRight()---接受一个函数作为累加器(accumulator)和数组的每个值(从右到左)将其减少为单个值。
Array.toLocaleString()---返回一个字符串表示数组中的元素。数组中的元素将使用各自的 toLocaleString 方法转成字符串,这些字符串将使用一个特定语言环境的字符串(例如一个逗号 ",")隔开。
Array.toString()---把数组转换为字符串,并返回结果。

可能会改变 也可能不会改变原数组的方法

Array.forEach()---对数组的每个元素执行一次给定的函数。
Array.map()---通过指定函数处理数组的每个元素,并返回处理后的数组。

会产生新数组的方法

Array.from()--从一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例。
Array.of()--创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型
Array.concat()--用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。
Array.filter()--创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。 
Array.flat()--会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。
Array.flatMap()--首先使用映射函数映射每个元素,然后将结果压缩成一个新数组
Array.map()--通过指定函数处理数组的每个元素,并返回处理后的数组。
Array.reduce()--对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。
Array.reduceRight()--接受一个函数作为累加器(accumulator)和数组的每个值(从右到左)将其减少为单个值。
Array.slice()---选取数组的的一部分,并返回一个新数组。

js对象

Object.assign(target, ...sources)--用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
Object.create()--创建一个新对象,使用现有的对象来提供新创建的对象的__proto__Object.defineProperties(obj, props)--直接在一个对象上定义新的属性或修改现有属性,并返回该对象。
Object.entries()--返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for...in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环还会枚举原型链中的属性)
eg:const object1 = {  
a: 'somestring',  
b: 42
};
for (const [key, value] of Object.entries(object1)) {  
console.log(`${key}: ${value}`);  // "a: somestring"  // "b: 42"
}
Object.fromEntries()--把键值对列表转换为一个对象。
Object.freeze()--可以冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。此外,冻结一个对象后该对象的原型也不能被修改。freeze() 返回和传入的参数相同的对象
Object.getOwnPropertyDescriptor()--返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)
Object.getOwnPropertyNames()--返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组
Object.getOwnPropertySymbols()--返回一个给定对象自身的所有 Symbol 属性的数组。
Object.getPrototypeOf() 方法返回指定对象的原型(内部[[Prototype]]属性的值)
Object.is()--判断两个值是否为同一个值。 
//都是 undefined、null、truefalse、相同长度的字符串且相同字符按相同顺序排列、相同对象(意味着每个对象有同一个引用)、都是数字且都是 +0或者都是 -0或者都是 NaN或都是非零而且非 NaN 且为同一个值
Object.preventExtensions()--让一个对象变的不可扩展,也就是永远不能再添加新的属性。
Object.isExtensible()--判断一个对象是否是可扩展的(是否可以在它上面添加新的属性)。
Object.keys()--会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致 。
eg:遍历对象:
    var obj = {'0':'a','1':'b','2':'c'};
    Object.keys(obj).forEach(function(key){ 
        console.log(key,obj[key]);
    });
Object.values()--返回一个给定对象自身的所有可枚举属性值的数组,值的顺序与使用for...in循环的顺序相同 ( 区别在于 for-in 循环枚举原型链中的属性 )。
eg:var obj = { 0: 'a', 1: 'b', 2: 'c' };
    console.log(Object.values(obj)); // ['a', 'b', 'c']​​

js对象属性排序

对象是一个散列结构本身是没有顺序的

var data = {
'a':'000',
'3':'ccc',
'1':'aaa',
'测试':'bbb',
};
Object.keys(data);
//控制台输出:["1", "3", "a", "测试"]
调整属性’a’和’测试’的顺序呢?
var data = {
'测试':'bbb',
'3':'ccc',
'a':'000',
'1':'aaa'
};
Object.keys(data);
//控制台输出:["1", "3", "测试", "a"]
结论:对象的遍历输出并不是按照属性的ASC码升序排序的。

Chrome Opera 的 JavaScript 解析引擎遵循的是新版 ECMA-262 第五版规范。因此,使用 for-in 语句遍历对象属性时遍历书序并非属性构建顺序。而 IE6 IE7 IE8 Firefox Safari 的 JavaScript 解析引擎遵循的是较老的 ECMA-262 第三版规范,属性遍历顺序由属性构建的顺序决定。

Chrome Opera 中使用 for-in 语句遍历对象属性时会遵循一个规律: 它们会先提取所有 key 的 parseFloat 值为非负整数的属性,然后根据数字顺序对属性排序首先遍历出来,然后按照对象定义的顺序遍历余下的所有属性。 其它浏览器则完全按照对象定义的顺序遍历属性。

如果想顺序遍历一组数据,请使用数组并使用 for 语句遍历。 for-in语句无法保证遍历顺序,应尽量避免编写依赖对象属性顺序的代码。如果想按照定义的次序遍历对象属性,请参考 这里 针对各浏览器编写特殊代码。 由于对象的输出是无序的,但是数组却是有序的,所以为了保证顺序,搞成数组再输出。

typeof与instanceof的区别

typeof

typeof 是判断参数是什么类型的实例,返回值为说明运算数类型的字符串。

返回值结果:“number”、“string”、“boolean”、“object”、“function”、“undefined”

若参数为引用类型,始终返回“object”,对于Array、null始终返回“object”,所以用typeof来判断参数类型有很大的局限性

instanceof

instanceof是用来判断一个对象在其原型链中是否存在一个构造函数的prototype属性,判断方法是根据对象的原型链依次向下查询,如果obj2的原型属性存在obj1的原型链上,(obj1 instanceof obj2)值为true

a instanceof b:判断a是否为b的实例,可以用于继承关系中

b是c的父对象,a是c的实例,a instanceof b 与 a instanceof c 结果均为true

JS 在获取当前月的最后一天

var date= new Date();
date.setMonth(date.getMonth() + 1);
let lastDay = date.setDate(0); // 日期设置为0号, 0表示1号的前一天
console.log('最后一天:' + new Date(lastDay).toLocaleString())

js校验重复事件段,并获取重复区间

 timeList = {    
    'DAILYMORE': [{      
            id: '',      
            startTime: '',
            endTime: '',      
            repeatValid: false,    
        }],    
    'WEEKLYMORE': [{      
        id: '',      
        startTime: '',      
        endTime: '',      
        repeatValid: false,    
    }],    
    'MONTHMORE': [{      
        id: '',      
        startTime: '',      
        endTime: '',      
        repeatValid: false,    
    }],  
    }; 
checkTimeList(type, element?) {    
    if (this.timeList[type].length > 1) {      
    let begin = [], over = [], repArr = [];      
    this.timeList[type].forEach(item => {        
        if (item.startTime && item.endTime) {          
            begin.push(item.startTime);          
            over.push(item.endTime);        
        }      
    });      
    begin = begin.sort();      
    over = over.sort();      
    over.forEach((item, i) => {        
    if (item > begin[i + 1]) {          
        repArr.push(item);        
    }      
    });      
    if (element) {        
        element['repeatValid'] = this.timeList[type].some((ele, i) => {     
         return repArr.includes(ele.endTime);      
        });      
    } else {        
        this.timeList[type].map((ele, i) => {         
             ele['repeatValid'] =  repArr.includes(ele.endTime);      
        });      
    }   
 } else {      
    this.timeList[type][0] ? this.timeList[type][0]['repeatValid'] = false : '';    
 }  
}

JS函数柯里化

柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。

函数柯里化的主要作用和特点就是参数复用、提前返回和延迟执行。

function curry(fn, args) {    var length = fn.length;    var args = args || [];    return function(){        newArgs = args.concat(Array.prototype.slice.call(arguments));        if (newArgs.length < length) {            return curry.call(this,fn,newArgs);        }else{            return fn.apply(this,newArgs);        }    }}function multiFn(a, b, c) {    return a * b * c;}var multi = curry(multiFn);multi(2)(3)(4);multi(2,3,4);multi(2)(3,4);multi(2,3)(4);​//es6const curry = (fn, arr = []) => (...args) => (  arg => arg.length === fn.length    ? fn(...arg)    : curry(fn, arg))([...arr, ...args])let curryTest=curry((a,b,c,d)=>a+b+c+d)curryTest(1,2,3)(4) //返回10curryTest(1,2)(4)(3) //返回10curryTest(1,2)(3,4) //返回10​

Flash提供了ExternalInterface接口与JavaScript通信,ExternalInterface有两个方法,call和addCallback:

  • ExternalInterface.addCallback("在js里可调用的flash方法名",flash内方法) //在flash中通过这个方法公开 在js中可调用的flash内的方法;

  • ExternalInterface.call("js方法",传给js的参数) //在flash里调用js里的方法

js设计模式

1.单例模式

单例模式即一个类只能构造出唯一实例,单例模式的意义在于共享、唯一

class SingletonLogin {
  constructor(name,password){
    this.name = name
    this.password = password
  }
  static getInstance(name,password){
    //判断对象是否已经被创建,若创建则返回旧对象
    if(!this.instance)this.instance = new SingletonLogin(name,password)
    return this.instance
  }
}
 
let obj1 = SingletonLogin.getInstance('CXK','123')
let obj2 = SingletonLogin.getInstance('CXK','321')
 
console.log(obj1===obj2)    // true
console.log(obj1)           // {name:CXK,password:123}
console.log(obj2)           // 输出的依然是{name:CXK,password:123}

2.观察者模式

//普通观察者模式
class Publisher{
    constructor(){
        this.observers=[];  //这里存放所有订阅者
        this.statement="";  //将来要发布的消息
    }
    //添加订阅者
    addob(ob){          
        if(this.observers.indexOf(ob)==-1){
            this.observers.push(ob);
        }
    }
    //移除订阅者
	removeob(ob){       
    	let idx =this.observers.indexOf(ob);
    	if(idx!=-1){
        	this.observers.splice(idx,1);
    	}
	}
	//发布消息
	notice(){       
   		this.observers.forEach((cur,idx)=>{
        	cur.update(this.statement);
    	});
	}
}
//订阅者
class Subscribe{  
    constructor(){}
	update(data){
    	console.log(data);
	}
}

let ob1 = new Subscribe();
let ob2 = new Subscribe();
let ob3 = {
   update(data){
        alert('hello'+data);
    }
}
let pb = new Publisher();
pb.addob(ob1);
pb.addob(ob2);
pb.addob(ob3);
pb.statement = "hello,everyone";
pb.notice();

//观察者模式升级版
class Publisher{
    constructor(){
    	//根据观察者订阅的主题分别存放观察者
        this.observers ={   
            any:[],
        }
    }
    //添加观察者,带类型,默认any类型
    addob(fn,type="any"){   
        if(!this.observers[type]){
            this.observers[type]=[];
        }
        if(this.observers[type].indexOf(fn)==-1){
            this.observers[type].push(fn);
        }
    }
    //默认any类型
	removeob(fn,type="any"){  
    	if(!this.observers[type]){
    		if(this.observers[type]){
        		let idx= this.observers[type].indexOf(fn);
        		if(idx!=-1){
            		this.observers[type].splice(idx, 1);
        		}
    		}
		}
	}
	//要发布的内容和所属类型
	notice(statement,type="any"){    
    	if(this.observers[type]){
        	this.observers[type].forEach(cur=>{
            	cur(statement);
        	});
    	}
	}
}

class Channel extends Publisher{
    constructor(){
        super();
    }
    //发布各类型消息
    film(){
        this.notice("movie");
    }
    tv(){
        this.notice("tv");
    }
}
//两个订阅者
var per1={
    callBack(data){
        console.log("收到tv:"+data);
    }
}
var per2={
    callBack(data){
        console.log("收到movie:"+data);
    }
}
//实例一个发布者
let channel = new Channel();
channel.addob(per1.callBack,"tv");
channel.addob(per2.callBack);   //默认any类型
channel.film();   //只发布film类型
channel.tv();   //只发布TV类型

ES6篇


Set 实例的属性和方法

set结构: {1,2,3,4,5} Object size:5

Set 结构的实例有以下属性。

  • Set.prototype.constructor:构造函数,默认就是Set函数。

  • Set.prototype.size:返回Set实例的成员总数。

Set 实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。下面先介绍四个操作方法。

  • add(value):添加某个值,返回 Set 结构本身。

  • delete(value):删除某个值,返回一个布尔值,表示删除是否成功。

  • has(value):返回一个布尔值,表示该值是否为Set的成员。

  • clear():清除所有成员,没有返回值。

  • keys():返回键名的遍历器

  • values():返回键值的遍历器

  • entries():返回键值对的遍历器

  • forEach():使用回调函数遍历每个成员

    let set = new Set([1, 2]); set.forEach(function(value, key, ownerSet) { console.log(key + " " + value); console.log(ownerSet === set); }); // 输出 1 1 true 2 2 true

Map 类型是键值对的有序列表,而键和值都可以是任意类型。

let map = new Map();
map.set("title", "Understanding ES6");
map.set("year", 2016);
console.log(map.get("title")); 
// "Understanding ES6"
console.log(map.get("year")); 
// 2016
  • has(key) :判断指定的键是否存在于 Map 中;

  • delete(key) :移除 Map 中的键以及对应的值;

  • clear() :移除 Map 中所有的键与值。

    let map = new Map([ ["name", "Nicholas"], ["age", 25]]); map.forEach(function(value, key, ownerMap) {
    console.log(key + " " + value);
    console.log(ownerMap === map); }); //打印:name Nicholastrueage 25true

async 和 await 和Promise

async 为异步函数, 是Generator 函数,await 只能出现在 async 函数中

async 函数返回的是一个 Promise 对象;await 可以用于等待一个 async 函数的返回值

async function test() {      ///Promise对象    
    const v1 = await getSomething();     ///相当于resolve(data || undefined)    
    const v2 = await testAsync();    
    console.log(v1, v2);
}
test().then(res=>{    
});

async/await 的优势在于处理 then 链

//三个步骤函数
function step1(n) {
    console.log(`step1 with ${n}`);
    return takeLongTime(n);
}
function step2(n) {
    console.log(`step2 with ${n}`);
    return takeLongTime(n);
}
function step3(n) {
    console.log(`step3 with ${n}`);
    return takeLongTime(n);
}
//Promise 方式来实现这三个步骤的处理
function doIt() {
    console.time("doIt");
    const time1 = 300;
    step1(time1)
        .then(time2 => step2(time2))
        .then(time3 => step3(time3))
        .then(result => {
            console.log(`result is ${result}`);
            console.timeEnd("doIt");
        });
}
doIt();
// async/await 实现
async function doIt() {
    console.time("doIt");
    const time1 = 300;
    const time2 = await step1(time1);
    const time3 = await step2(time2);
    const result = await step3(time3);
    console.log(`result is ${result}`);
    console.timeEnd("doIt");
}
doIt();

promise链式操作

Promise的精髓是“状态”,用维护状态、传递状态的方式来使得回调函数能够及时调用,它比传递callback函数要简单、灵活的多。所以使用Promise的正确场景是这样的

runAsync1()
.then(function(data){
    console.log(data);
    return runAsync2();
})
.then(function(data){
    console.log(data);
    return runAsync3();
})
.then(function(data){
    console.log(data);
});

promise构造函数是同步执行的,then方法是异步执行的

Typescript篇

TypeScript

JavaScript

JavaScript 的超集用于解决大型项目的代码复杂性

一种脚本语言,用于创建动态网页

可以在编译期间发现并纠正错误

作为一种解释型语言,只能在运行时发现错误

强类型,支持静态和动态类型

弱类型,没有静态类型选项

最终被编译成 JavaScript 代码,使浏览器可以理解

可以直接在浏览器中使用

支持模块、泛型和接口

不支持模块,泛型或接口

社区的支持仍在增长,而且还不是很大

大量的社区支持以及大量文档和解决问题的支持

安装:npm install -g typescript验证版本:tsc -v编译:tsc helloworld.ts

基础类型

Boolean 类型:let isDone: boolean = false;
Number 类型:let count: number = 10;
String 类型:let name: string = "semliker";
Symbol 类型:
	const sym = Symbol();
	let obj = {
  		[sym]: "semlinker",
	};
	console.log(obj[sym]); // semlinker 
Array 类型:
	let list: number[] = [1, 2, 3];
	let list: Array<number> = [1, 2, 3]; // Array<number>泛型语法
Enum 类型:
enum Direction {
  NORTH,
  SOUTH,
  EAST,
  WEST,
} //默认情况下,NORTH 的初始值为 0,其余的成员会从 1 开始自动增长
let dirName = Direction[0]; // NORTH
let dirVal = Direction["NORTH"]; // 0
enum Color {Red = 1, Green, Blue}
let colorName: string = Color[2];
console.log(colorName); //Green
Any 类型:
let notSure: any = 666;
notSure = "semlinker";
notSure = false;
Unknown 类型:unknown 类型只能被赋值给 any 类型和 unknown 类型本身
let value: unknown;
let value1: unknown = value; // OK
let value2: any = value; // OK
let value3: boolean = value; // Error
let value4: number = value; // Error
let value5: string = value; // Error
let value6: object = value; // Error
let value7: any[] = value; // Error
let value8: Function = value; // Error
Tuple 类型:数组一般由同种类型的值组成,但有时我们需要在单个变量中存储不同类型的值,这时候我们就可以使用元组
let tupleType: [string, boolean];
tupleType = ["semlinker", true];
Void 类型:
let unusable: void = undefined;
// 声明函数返回值为void
function warnUser(): void {
  console.log("This is my warning message");
}
Null 和 Undefined 类型:
let u: undefined = undefined;
let n: null = null;
object, Object 和 {} 类型:
interface ObjectConstructor {
  create(o: object | null): any;
}
Object.create(null);
const obj = {};
Never 类型:never 类型表示的是那些永不存在的值的类型
// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
  throw new Error(message);
}

函数:

TypeScript

JavaScript

含有类型

无类型

箭头函数

箭头函数(ES2015)

函数类型

无函数类型

必填和可选参数

所有参数都是可选的

默认参数

默认参数

剩余参数

剩余参数

函数重载

无函数重载

1、函数定义

// 函数声明写法
function maxA(x: number, y: number): number {
    return x > y? x : y;
}

// 函数表达式写法
let maxB = function(x: number, y: number): number {
    return x > y? x : y;
}

2、可选参数

function max(x: number, y?: number): number {
}

3.默认参数

function max(x: number, y = 4): number {    
    return x > y? x: y;
}​
let result1 = max(2);    // 正常
let result2 = max(2, 4);    // 正常
let result3 = max(2, undefined);    // 正常

4.剩余参数

function sum(x:number, ...restOfNumber:number[]): number {
}

5.函数重载

function css(config: {});
function css(config: string, value: string);

泛型:允许同一个函数接受不同类型参数的一种模板

设计泛型的关键目的是在成员之间提供有意义的约束,这些成员可以是:类的实例成员、类的方法、函数参数和函数返回值。

function identity <T, U>(value: T, message: U) : T {
  console.log(message);
  return value;
}
console.log(identity<Number, string>(68, "Semlinker"));
//泛型约束
interface Lengthwise {
    length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);  // Now we know it has a .length property, so no more error
    return arg;
}

装饰器

一个表达式,该表达式被执行后,返回一个函数,函数的入参分别为 target、name 和 descriptor,执行该函数后,可能返回 descriptor 对象,用于配置 target 对象

类装饰器

function Greeter(greeting: string) {
  return function (target: Function) {
    target.prototype.greet = function (): void {
      console.log(greeting);
    };
  };
}
@Greeter("Hello TS!")
class Greeting {
  constructor() {
    // 内部实现
  }
}
let myGreeting = new Greeting();
(myGreeting as any).greet(); // console output: 'Hello TS!';

网络篇--http

一、HTTP最常见的请求头如下:

l Accept:浏览器可接受的MIME类型;

l Accept-Charset:浏览器可接受的字符集;

l Accept-Encoding:浏览器能够进行解码的数据编码方式,比如gzip。Servlet能够向支持gzip的浏览器返回经gzip编码的HTML页面。许多情形下这可以减少5到10倍的下载时间;

l Accept-Language:浏览器所希望的语言种类,当服务器能够提供一种以上的语言版本时要用到;

l Authorization:授权信息,通常出现在对服务器发送的WWW-Authenticate头的应答中;

l Connection:表示是否需要持久连接。如果Servlet看到这里的值为“Keep-Alive”,或者看到请求使用的是HTTP 1.1(HTTP 1.1默认进行持久连接),它就可以利用持久连接的优点,当页面包含多个元素时(例如Applet,图片),显著地减少下载所需要的时间。要实现这一点,Servlet需要在应答中发送一个Content-Length头,最简单的实现方法是:先把内容写入ByteArrayOutputStream,然后在正式写出内容之前计算它的大小;

l Content-Length:表示请求消息正文的长度;

l Cookie:这是最重要的请求头信息之一;

l From:请求发送者的email地址,由一些特殊的Web客户程序使用,浏览器不会用到它;

l Host:初始URL中的主机和端口;

l If-Modified-Since:只有当所请求的内容在指定的日期之后又经过修改才返回它,否则返回304“Not Modified”应答;

l Pragma:指定“no-cache”值表示服务器必须返回一个刷新后的文档,即使它是代理服务器而且已经有了页面的本地拷贝;

l Referer:包含一个URL,用户从该URL代表的页面出发访问当前请求的页面。

l User-Agent:浏览器类型,如果Servlet返回的内容与浏览器类型有关则该值非常有用;

l UA-Pixels,UA-Color,UA-OS,UA-CPU:由某些版本的IE浏览器所发送的非标准的请求头,表示屏幕大小、颜色深度、操作系统和CPU类型。

二、响应头

HTTP最常见的响应头如下所示:

l Allow:服务器支持哪些请求方法(如GET、POST等);

l Content-Encoding:文档的编码(Encode)方法。只有在解码之后才可以得到Content-Type头指定的内容类型。利用gzip压缩文档能够显著地减少HTML文档的下载时间。Java的GZIPOutputStream可以很方便地进行gzip压缩,但只有Unix上的Netscape和Windows上的IE 4、IE 5才支持它。因此,Servlet应该通过查看Accept-Encoding头(即request.getHeader("Accept-Encoding"))检查浏览器是否支持gzip,为支持gzip的浏览器返回经gzip压缩的HTML页面,为其他浏览器返回普通页面;

l Content-Length:表示内容长度。只有当浏览器使用持久HTTP连接时才需要这个数据。如果你想要利用持久连接的优势,可以把输出文档写入ByteArrayOutputStram,完成后查看其大小,然后把该值放入Content-Length头,最后通过byteArrayStream.writeTo(response.getOutputStream()发送内容;

l Content-Type: 表示后面的文档属于什么MIME类型。Servlet默认为text/plain,但通常需要显式地指定为text/html。由于经常要设置Content-Type,因此HttpServletResponse提供了一个专用的方法setContentTyep。 可在web.xml文件中配置扩展名和MIME类型的对应关系;

l Date:当前的GMT时间。你可以用setDateHeader来设置这个头以避免转换时间格式的麻烦;

l Expires:指明应该在什么时候认为文档已经过期,从而不再缓存它。

l Last-Modified:文档的最后改动时间。客户可以通过If-Modified-Since请求头提供一个日期,该请求将被视为一个条件GET,只有改动时间迟于指定时间的文档才会返回,否则返回一个304(Not Modified)状态。Last-Modified也可用setDateHeader方法来设置;

l Location:表示客户应当到哪里去提取文档。Location通常不是直接设置的,而是通过HttpServletResponse的sendRedirect方法,该方法同时设置状态代码为302;

l Refresh:表示浏览器应该在多少时间之后刷新文档,以秒计。除了刷新当前文档之外,你还可以通过setHeader("Refresh", "5; URL=http://host/path")让浏览器读取指定的页面。注意这种功能通常是通过设置HTML页面HEAD区的实现,这是因为,自动刷新或重定向对于那些不能使用CGI或Servlet的HTML编写者十分重要。但是,对于Servlet来说,直接设置Refresh头更加方便。注意Refresh的意义是“N秒之后刷新本页面或访问指定页面”,而不是“每隔N秒刷新本页面或访问指定页面”。因此,连续刷新要求每次都发送一个Refresh头,而发送204状态代码则可以阻止浏览器继续刷新,不管是使用Refresh头还是<META HTTP-EQUIV="Refresh" ...>。注意Refresh头不属于HTTP 1.1正式规范的一部分,而是一个扩展,但Netscape和IE都支持它。

三、实体头

实体头用坐实体内容的元信息,描述了实体内容的属性,包括实体信息类型,长度,压缩方法,最后一次修改时间,数据有效性等。

l Allow:GET,POST

l Content-Encoding:文档的编码(Encode)方法,例如:gzip,见“2.5 响应头”;

l Content-Language:内容的语言类型,例如:zh-cn;

l Content-Length:表示内容长度,eg:80,可参考“2.5响应头”;

l Content-Location:表示客户应当到哪里去提取文档,例如:www.dfdf.org/dfdf.html,可参考“2.5响应头”;

l Content-MD5:MD5 实体的一种MD5摘要,用作校验和。发送方和接受方都计算MD5摘要,接受方将其计算的值与此头标中传递的值进行比较。Eg1:Content-MD5: <base64 of 128 MD5 digest>。Eg2:dfdfdfdfdfdfdff==;

l Content-Range:随部分实体一同发送;标明被插入字节的低位与高位字节偏移,也标明此实体的总长度。Eg1:Content-Range: 1001-2000/5000,eg2:bytes 2543-4532/7898

l Content-Type:标明发送或者接收的实体的MIME类型。Eg:text/html; charset=GB2312 主类型/子类型;

l Expires:为0证明不缓存;

l Last-Modified:WEB 服务器认为对象的最后修改时间,比如文件的最后修改时间,动态页面的最后产生时间等等。例如:Last-Modified:Tue, 06 May 2008 02:42:43 GMT.

四、扩展头

在HTTP消息中,也可以使用一些再HTTP1.1正式规范里没有定义的头字段,这些头字段统称为自定义的HTTP头或者扩展头,他们通常被当作是一种实体头处理。

现在流行的浏览器实际上都支持Cookie,Set-Cookie,Refresh和Content-Disposition等几个常用的扩展头字段。

l Refresh:1;url=www.dfdf.org //过1秒跳转到指定位置;

l Content-Disposition:头字段,可参考“2.5响应头”;

l Content-Type:WEB 服务器告诉浏览器自己响应的对象的类型。

eg1:Content-Type:application/xml ;

eg2:applicaiton/octet-stream;

跨域

同源策略限制以下几种行为
1.) Cookie、LocalStorage 和 IndexDB 无法读取
2.) DOM 和 Js对象无法获得
3.) AJAX 请求不能发送

解决方案

1、 通过jsonp跨域 

2、 document.domain + iframe跨域 

3、 location.hash + iframe 

4、 window.name + iframe跨域 

5、 postMessage跨域 

6、 跨域资源共享(CORS) 

7、 nginx代理跨域 

8、 nodejs中间件代理跨域

 9、 WebSocket协议跨域

from跟踪数值状态

at():获取数组中指定 index 处的 AbstractControl

push():在数组的末尾插入一个新的 AbstractControl

insert():在数组中的指定 index 处插入一个新的 AbstractControl。insert(index: number, control: AbstractControl): void

removeAt():移除位于数组中的指定 index 处的控件。removeAt(index: number): void

setValue():设置此 FormArray 的值。它接受一个与控件结构相匹配的数组

patchValue():修补此 FormArray 的值。它接受一个与该控件的结构相匹配的数组,并尽量把它们的值匹配到组中正确的控件上。patchValue(value: any[], options: { onlySelf?: boolean; emitEvent?: boolean; } = {}): void

reset():重置这个 FormArray,把它的各级子控件都标记为 pristineuntouched,并把它们的值都设置为 null

getRawValue():这个 FormArray 的聚合值,包括所有已禁用的控件。

clear():移除表单里边的所有控件

Chrome浏览器不支持字体小于12px的解决办法

transform:scale()

-webkit-transform:scale(0.833);
/*这个数字0.833,是缩放比例,可以根据你自己的情况不同而不同。*/

Chrome禁用自动填充

<input type="password" matInput autocomplete="new-password" formControlName="password">
autocomplete="new-username"   
autocomplete="new-password"

特性

Cookie

localStorage

sessionStorage

数据的生命期

可设置失效时间,默认是关闭浏览器后失效

除非被清除,否则永久保存

仅在当前会话下有效,关闭页面或浏览器后被清除

存放数据大小

4K左右

一般为5MB

一般为5MB

与服务器端通信

每次都会携带在HTTP头中,如果使用cookie保存过多数据会带来性能问题

仅在客户端(即浏览器)中保存,不参与和服务器的通信

仅在客户端(即浏览器)中保存,不参与和服务器的通信

易用性

需要程序员自己封装,源生的Cookie接口不友好

源生接口可以接受,亦可再次封装来对Object和Array有更好的支持

源生接口可以接受,亦可再次封装来对Object和Array有更好的支持

requireimport的区别

  • require支持 动态导入import不支持,正在提案 (babel 下可支持)

  • require同步 导入,import属于 异步 导入

  • require值拷贝,导出值变化不会影响导入值;import指向 内存地址,导入值会随导出值而变化

Web Worker 使用教程

www.ruanyifeng.com/blog/2018/0…

vue

V-model的原理是什么?

Vue的双向数据绑定是由数据劫持结合发布者订阅者实现的。 数据劫持是通过Object.defineProperty()来劫持对象数据的setter和getter操作。 在数据变动时作你想做的事

原理 通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化->视图更新 在初始化vue实例时,遍历data这个对象,给每一个键值对利用Object.definedProperty对data的键值对新增get和set方法,利用了事件监听DOM的机制,让视图去改变数据

vuex的流程

页面通过mapAction异步提交事件到action。action通过commit把对应参数同步提交到mutation。 mutation会修改state中对于的值。 最后通过getter把对应值跑出去,在页面的计算属性中 通过mapGetter来动态获取state中的值

vuex有哪几种状态和属性

state中保存着共有数据,数据是响应式的 getter可以对state进行计算操作,主要用来过滤一些数据,可以在多组件之间复用 mutations定义的方法动态修改state中的数据,通过commit提交方法,方法必须是同步的 actions将mutations里面处理数据的方法变成异步的,就是异步操作数据,通store.dispatch来分发actions,把异步的方法写在actions中,通过commit提交mutations,进行修改数据。 modules:模块化vuex

Angular

1.RouteReuseStrategy路由复用策略

只能依赖注入到根模块,进行路由配置后,可存储对应组件

shouldDetach 是否允许复用路由

store 当路由离开时会触发,存储路由

shouldAttach 是否允许还原路由

retrieve 获取存储路由

shouldReuseRoute 进入路由触发,是否同一路由时复用路由

//reuse-stategy.ts
import { ActivatedRouteSnapshot, RouteReuseStrategy, DetachedRouteHandle, Router, NavigationEnd } from '@angular/router';

export class CustomReuseStrategy implements RouteReuseStrategy {

  public static handlers: { [key: string]: DetachedRouteHandle } = {};
  subscribe;
  currentUrl;
  public static shouldStore = false;
  constructor(
  ) {
  }
  /** 删除缓存路由快照的方法 */
  public static deleteRouteSnapshot(path: string) {
    for (const key in CustomReuseStrategy.handlers) {
      if (new RegExp(path, 'g').test(key)) {
        delete CustomReuseStrategy.handlers[key];
      }
    }
    CustomReuseStrategy.shouldStore = false;
  }
  /**
   * @description: 是否允许复用路由
   * @param {type}
   * @return:
   */
  public shouldDetach(route: ActivatedRouteSnapshot): boolean {
    if (!!route.data && !!route.data.reusePath) {
      return true;
    }
    return false;
  }

  /**
   * @description: 当路由离开时会触发,存储路由
   * @param {type}
   * @return:
   */
  public store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
    if (CustomReuseStrategy.shouldStore) {
      CustomReuseStrategy.handlers[route.data.reusePath] = handle;
    }
  }

  /**
   * @description: 是否允许还原路由
   * @param {type}
   * @return:
   */
  public shouldAttach(route: ActivatedRouteSnapshot): boolean {
    if (!!route.data.reusePath && !!CustomReuseStrategy.handlers[route.data.reusePath]) {
      return true;
    }
    return false;
  }

  /**
   * @description: 获取存储路由
   * @param {type}
   * @return:
   */
  public retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
    if (!!route.data && !!route.data.reusePath) {
      return CustomReuseStrategy.handlers[route.data.reusePath];
    }
    return null;
  }

  /**
   * @description: 进入路由触发,是否同一路由时复用路由
   * @param {type}
   * @return:
   */
  public shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
    if (future.data && curr.data && future.data.reusePath && curr.data.reusePath) {
      return future.data.reusePath === curr.data.reusePath;
    }
    return false;
  }
}

//app.module.ts
import { RouteReuseStrategy, RouterModule } from '@angular/router';
import { CustomReuseStrategy } from './';
providers: [
    {provide: RouteReuseStrategy, useClass: CustomReuseStrategy}
  ],

//路由配置--根/子路由都可
{
    path: '', component: TaskListComponent,
    data: { reusePath: 'task/list' }   //进行路由存储配置
  },
//.ts
storerouter(){
    CustomReuseStrategy.shouldStore = true;   //允许路由存储
}

material UI

1.配置

npm install --save @angular/material @angular/cdk

npm install --save @angular/animations

npm install --save hammerjs

// main.ts
import 'hammerjs';
​// style.css
@import "~@angular/material/prebuilt-themes/indigo-pink.css";
/*如果需要md-icon需要引入*/
@import '~https://fonts.googleapis.com/icon?family=Material+Icons';

2.关于选择多个MatAutocompleteTrigger

html
<mat-form-field class="input">
          <label matPrefix>类别:</label>
          <input matInput formControlName="category" #autoInput [matAutocomplete]="auto" (input)="cateInput($event, 1)"
            (click)="openAuto($event, 1)">
          <mat-autocomplete #auto="matAutocomplete" (optionSelected)="selectCate($event, 1)">
            <mat-option *ngFor="let key of objectKeys(list)" [value]="list[key]">
              {{list[key]}}
            </mat-option>
          </mat-autocomplete>
        </mat-form-field>
         <mat-form-field class="input">
          <label matPrefix>组名:</label>
          <input matInput formControlName="groupName" #groupInput [matAutocomplete]="group"
            (input)="cateInput($event, 0)" (click)="openAuto($event, 0)">
          <mat-autocomplete #group="matAutocomplete" (optionSelected)="selectCate($event, 0)">
            <mat-option *ngFor="let key of objectKeys(list)" [value]="key">
              {{key}}
            </mat-option>
          </mat-autocomplete>
        </mat-form-field>
TS:
@ViewChild('autoInput', { read: MatAutocompleteTrigger }) auto: MatAutocompleteTrigger;
@ViewChild('groupInput', { read: MatAutocompleteTrigger }) group: MatAutocompleteTrigger;

      this.auto.openPanel();
      this.group.openPanel();

React

1.setState为异步

 constructor() {
        super();
        this.state = {
            count: 1
        };
    }
//异步
this.setState({count: this.state.count+1})    // 2,3,4,5
console.log(this.state.count);				//1,2,3,4

//改为同步
//1)
this.setState({count: this.state.count+1},()=>{
    console.log(this.state.count);    //2,3,4,5
})
//2)
async increment(){
    await this.setStateAsync({count: this.state.count + 1})
    console.log(this.state.count);    //2,3,4,5
}
setStateAsync(state){
    return new Promise(()=>{
        this.setState(state, resolve);
    });
}