4.类型检测
8.常用正则表达式
11.组件中定义的函数this是什么?通过bind和箭头函数怎么添加到类的实例对象上并直接使用
12.为什么不能直接修改state,immutable的state和默认的有什么不同?state和props的使用场景分别是怎么的?
13.Key值的相关问题
15.数据的初始化
3.Emoji表情包
:sparkling_heart:一起记笔记呀~
之前在某项目中需要兼容IE,而部分模块在IE下会报错,不得已需要根据浏览器来引用不同的模块,emmm当时没遇到过这种,然后写出了如下写法,不出意外,报错了
...
if (IE) {
import xx from 'xx'
}else{
import cc from 'cc'
}
...
然后去google了一下发现,由于import
是静态执行,所以不能使用表达式和变量,以及只有在运行时才能得到结果的语法结构比如if
。
何为变量import:
const a ='module'
import { b } from a
何为表达式import:
import { 'a'+'b'} from 'module'
然后尝试了另一种写法
...
if (IE) {
const a = require('a')
} else {
const b = require('b')
}
...
嗯,这种用require
引用没有问题,嗯,然后这个问题就到此结束了,后来去看ruanyifeng的ES6时发现新增了一个Import()
方法去动态加载
...
if (IE) {
import('a')
} else {
import('b')
}
...
具体的内容可以看一下阮一峰写的ES6,很详细ruanyifeng ES6
先看个例子
function get() {
return 5
}
var a = get
console.log(a)
//ƒ() { return 5 }
var b = get()
console.log(b)
//5
可以看出var a=get
传递的是函数对象,此时a
与get
是等价的,而var b=get()
传递是函数的返回值,此时b
是get()
的返回值。
简单来说加括号时,是调用该函数值为函数的返回值。
不加括号可认为是查看函数完整信息,即查看整个函数体,返回值即整个函数体。
JavaScript中定义函数主要有三种方式:
1. 函数声明(Function Declaration)
function a(){ ... }
a
会被提升,也就是同一个作用域中可以在该函数定义之前调用它。2. 函数表达式(Function Expression)
var a = function(){ .... }
undefined
。3. Function 构造函数
new Function(变量1,变量2,...,函数体)
label
标签"Can I use" provides up-to-date browser support tables for support of front-end web technologies on desktop and mobile web browsers.
The site was built and is maintained by Alexis Deveria, with occasional updates provided by the web development community. The design used as of 2014 was largely created by Lennart Schoors.
- Generate screenshots and PDFs of pages.
- Crawl a SPA (Single-Page Application) and generate pre-rendered content (i.e. "SSR" (Server-Side Rendering)).
- Automate form submission, UI testing, keyboard input, etc.
- Create an up-to-date, automated testing environment. Run your tests directly in the latest version of Chrome using the latest JavaScript and browser features.
- Capture a timeline trace of your site to help diagnose performance issues.
- Test Chrome Extensions.
Stack Overflow is an open community for anyone that codes. We help you get answers to your toughest coding questions, share knowledge with your coworkers in private, and find your next dream job.
嗯,不会的直接去搜就对了:smile:
看到一个很形象的解释
假设我们有一个数组,每个元素是一个人。你面前站了一排人。foreach
就是你按顺序一个一个跟他们做点什么,具体做什么,随便:
people.forEach( dude => {
dude.pickUpSoap();
});
map
就是你手里拿一个盒子(一个新的数组),一个一个叫他们把钱包扔进去。结束的时候你获得了一个新的数组,里面是大家的钱包,钱包的顺序和人的顺序一一对应。
var wallets = people.map(dude => {
return dude.wallet;
});
reduce
就是你拿着钱包,一个一个数过去看里面有多少钱啊?每检查一个,你就和前面的总和加一起来。这样结束的时候你就知道大家总共有多少钱了。
var totalMoney = wallets.reduce((countedMoney, wallet) => {
return countedMoney + wallet.money;
}, 0);
还有一个filter
:
你一个个钱包数过去的时候,里面钱少于 100 块的不要(留在原来的盒子里),多于 100 块的丢到一个新的盒子里。这样结束的时候你又有了一个新的数组,里面是所有钱多于 100 块的钱包。
var fatWallets = wallets.filter(wallet => {
return wallet.money > 100;
});
其中map
和 filter
都是 immutable methods
,也就是说它们只会返回一个新数组,而不会改变原来的那个数组。
要检测一个变量是不是基本数据类型,最常用的方法是用typeof
var string = 'dogtiti'
var num = 123
var bol = true
var u
var f = function () { }
var n = null
var obj = new Object()
var date= new Date()
var reg = new RegExp()
console.log(typeof string) //string
console.log(typeof num) //number
console.log(typeof bol) //boolean
console.log(typeof u) //undefined
console.log(typeof f) //function
console.log(typeof n) //object
console.log(typeof obj) //object
console.log(typeof date) //object
console.log(typeof reg) //object
然后发现后面四个返回的都是object
,那么怎么知道它是什么类型的对象呢?
查阅红宝书(JavaScript高级程序设计第三版)是如下说明的
ECMAScript提供了instanceof操作符,其语法如下
result = variable instanceof constructor
根据规定,所有引用类型(根据它的原型链来识别)的值都是Object
的实例。因此,在检测一个引用类型值和Object
构造函数时,instanceof
操作符始终会返回true,当然,如果使用instanceof
操作符检测基本类型的值,该操作符始终返回false
,因为基本类型不是对象。
console.log(obj instanceof Object) //true
console.log(date instanceof Date) //true
console.log(reg instanceof RegExp) //true
然后设计到原型链
的知识推荐两篇文章,写的比较详细
在 Lodash 的代码中,会经常看到这样的代码片段,需要根据传入的 Array 的初始和结束索引求得要操作的数据元素的长度。
那 >>> 0 有什么用处?
在 JS 中,Array.length 是一个 32 位无符号整型数字,而通过无符号位移运算 >>> 0,就是为了确保我们得到的正确的 length 值,它总是能得到一个 32-bit unsigned ints ... 比如:
-1 >>> 0 // 4294967295
0 >>> 0 // 0
'1' >>> 0 // 1
'1x' >>> 0 // 0
null >>> 0 // 0
而且,对于异常情况的包容也可以让我们减少一些类型判断和显示的强制类型转化的操作。
因此,在有用到 Array.length 的场景,可用 >>> 0 做参数防护。
我们会用 instanceof 去判断实例与类之间的关系,但是这种关系可靠吗?
arr instanceof Array 为 true 肯定 arr 就是一个数组,但是为 false 是不是就表示 arr 肯定不是数组呢?
不能!在有多个 frame 的时候就不能...
let xArray = window.frames[0].Array; // origin from iframe's window
let arr = new xArray(1, 2, 3);
arr instanceof Array; // false
Array.isArray(arr); // true
虽然我们平时不常会有多个 frame 间这种级别的类的混用,但是以防万一
通常,在需要判断是否是数组时,可以使用 Array.isArray 或者它的 Polyfill:
if (!Array.isArray) {
Array.isArray = function(arg) {
return Object.prototype.toString.call(arg) === '[object Array]';
};
}
判断 value 和 other 是否(强)相等,在 ECMA 规范中对于相等其实是有明确的定义的,其中 NaN === NaN,但是在 JS 中,NaN 是不与任何值相等的,包括自身,而 Lodash 弥补了这部分的不足,那怎么判断 NaN === NaN 呢?
function eq(value, other) {
return value === other || (value !== value && other !== other)
}
我们知道 NaN 是唯一个不与自身相等的值,因此可以通过这个特性分别得到 value 和 other 是否是 NaN,如果是,则两者相等,返回 true.
如何判断是否是 NaN?
通过 isNaN 全局方法?
isNaN('x') // true
isNaN(NaN) // true
isNaN(0) // false
全局方法 isNaN 是有坑的,它的 NaN 定义估计真的是 not a number, 而不是值得 NaN 这个s数值,也就是除了 NaN, 对于 不能转化为数字 的情况都会返回 true ... 其实这个问题已经在 ES6 中被改善啦,Number.isNaN 只会对 NaN 返回 true, 其他情况都是 false...
-0 的 toString 竟然没有保留 -
但是,_.toString(-0) === '-0' ,如何做的?
// 一系列的前置检查和转化后...
const INFINITY = 1 / 0; // 手动 INFINITY
const res = `${value}`;
if (res === '0' && 1 / value === -INFINITY) {
return '-0';
} else {
return res;
}
数字、数字字符串肯定是可以的...
对象?或者非数字的字符串?它们也能得到 NaN
Symbol !
Number('xx') // NaN
Number(new Object()) // NaN
Number(Symbol())
// --> Uncaught TypeError: Cannot convert a Symbol value to a number
另外,Symbol 可以显示的转化为 String 和 Boolean, 但是,不能进行任何隐式转换,也就是不能对 Sybmol 进行运算,比如:
const symbol = Symbol();
1 + symbol // TypeError
'1' + symbol // TypeError
// 可显示转化为 布尔 和 字符串
Boolean(symbol) // true
String(symbol) // "Symbol()"
_.repeat([str = ''], [times = 1])
指定 string 重复 n 次输出,可以很简单的通过循环实现:
const repeat = (str = '', times = 1) => {
let res = str;
while (--times) {
res += str;
}
return res;
}
repeat('6', 3); // 666
设想 repeat('6', 4) 按照上述实现需要循环 4 次,但是其实是可以通过两次操作就把最终结果拼接出来的,就相当于 repeat(repeat('6', 2), 2) ,所以这块是有优化空间的,来看优化后的算法:
const repeat = (str = '', times = 1) => {
let result = '';
if (!str || times < 1) {
return result;
}
do {
if (times % 2) {
result += str;
}
times = Math.floor(times / 2);
if (times) {
str += str;
}
} while (times)
return result;
}
要将变量强制转换为布尔值而不更改其值:
const myBoolean = !! myVariable;
!!null // false
!!undefined // false
!!false // false
!!ture // ture
!!"" // false
!!"string" // true
!!0 // false
!!1 // true
!!{} // true
!![] // true
要使用spread运算符有条件地在对象上设置属性:
const myObject = {... myProperty && {propName:myPoperty}};
let myProperty = 'Jhon'
const myObject = {...myProperty && {propName: myProperty}}; // {propName: "Jhon"}
let myProperty = ''
const myObject = {...myProperty && {propName: myProperty}}; // {}
如果myProperty结果为false,则 && 失败并且不设置新属性; 否则,如果不为空,&& 将设置新属性并覆盖原来的值。
const mergedObject = { ...objectOne, ...objectTwo };
const mergedObject = { ...{name: 'Jhon', age: '18'}, ...{name1: 'jhon1', age1: '12'}};
// {name: "Jhon", age: "18", name1: "jhon1", age1: "12"}
const mergedObject = { ...{name: 'Jhon', age: '18'}, ...{name: 'jhon1', age:'12'}};
// {name: "jhon1", age: "12"}
支持无限制合并,但如果对象之间存在相同属性,则后面属性会覆盖前面属性。
请注意,这仅适用于浅层合并。
要在不使用中间变量的情况下交换两个变量的值:
[varA,varB] = [varB,varA];
let a = 1;
let b = 2;
[a, b] = [b, a] // a = 2 b = 1
const clean = [0, false, true, undefined, null, '', 12, 15].filter(Boolean);
// [true, 12, 15]
这将删除值等于:null,undefined,false,0 和空字符串('')。
要将Number元素转换为String元素:
const stringArray = numberArray.map(String);
const stringArray = [1, 2, 3].map(String);
["1", "2", "3"]
如果数组包含字符串,字符串原样保留。 这也可以用于将String元素转换为Number类型:
const numberArray = stringArray.map(Number);
const stringArray = ["1", "2", "3"].map(String);
// [1, 2, 3]
要以可读的格式显示JSON代码:
const formatted = JSON.stringify(myObj, null, 4);
const formatted = JSON.stringify({name: 'Jhon', age: 18, address: 'sz'}, null, 4);
/*
{
"name": "Jhon",
"age": 18,
"address": "sz"
}
*/
该字符串化命令有三个参数。第一个是Javascript对象。第二个是可选函数,可用于在JSON进行字符串化时对其执行操作。最后一个参数指示要添加多少空格作为缩进以格式化JSON。省略最后一个参数,JSON将返回一个长行。如果myObj中存在循环引用,则会格式失败。
要创建一个数组并用数字填充它,索引为零:
const numArray = Array.from(new Array(10), (x, i)=> i);
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
const code = Math.floor(Math.random() * 1000000).toString().padStart(6, "0");
// 942377
const IDReg= /(^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$)|(^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{2}[0-9Xx]$)/;
有时候我们会对url的查询参数即从问号 (?)后 开始的 URL(查询部分)进行转换
const searchObj = search => JSON.parse(`{"${decodeURIComponent(search.substring(1)).replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g, '":"')}"}`);
// 假如请求url为
// 'https://www.baidu.com?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=js&rsv_pq=a86b5e5f0007bceb&rsv_t=1e1fAVan%2BVlnkhJHFB0BIGLdLM2slszYMJBTTfFkmyyBUzBpw0ggeuVDE50&rqlang=cn&rsv_enter=0&inputT=1287&rsv_sug3=5&rsv_sug1=3&rsv_sug7=101&rsv_sug2=0&rsv_sug4=1907'
// 那么 window.location.search 就为:
let search = '?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=js&rsv_pq=a86b5e5f0007bceb&rsv_t=1e1fAVan%2BVlnkhJHFB0BIGLdLM2slszYMJBTTfFkmyyBUzBpw0ggeuVDE50&rqlang=cn&rsv_enter=0&inputT=1287&rsv_sug3=5&rsv_sug1=3&rsv_sug7=101&rsv_sug2=0&rsv_sug4=1907'
searchObj(search)
/*
f: "8"
ie: "utf-8"
inputT: "1287"
rqlang: "cn"
rsv_bp: "1"
rsv_enter: "0"
rsv_idx: "1"
rsv_pq: "a86b5e5f0007bceb"
rsv_sug1: "3"
rsv_sug2: "0"
rsv_sug3: "5"
rsv_sug4: "1907"
rsv_sug7: "101"
rsv_t: "1e1fAVan+VlnkhJHFB0BIGLdLM2slszYMJBTTfFkmyyBUzBpw0ggeuVDE50"
tn: "baidu"
wd: "js"
*/
const objectToQueryString = (obj) => Object.keys(obj).map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(obj[key])}`).join('&');
objectToQueryString({name: 'Jhon', age: 18, address: 'beijing'})
// name=Jhon&age=18&address=beijing
const similarity = (arr, values) => arr.filter(v => values.includes(v));
similarity([1, 2, 3], [1, 2, 4]); // [1,2]
使用正则表达式来检测 navigator.userAgent 属性判断设备是在移动设备还是在台式机/笔记本电脑打开。
const detectDeviceType = () =>/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|OperaMini/i.test(navigator.userAgent) ? 'Mobile' : 'Desktop';
const toDecimalMark = num => num.toLocaleString('en-US');
toDecimalMark(12305030388.9087); // "12,305,030,388.909"
const deepFlatten = arr => [].concat(...arr.map(v => (Array.isArray(v) ? deepFlatten(v) : v)));
deepFlatten([1, [2], [[3], 4], 5]); // [1,2,3,4,5]
const reducedFilter = (data, keys, fn) =>data.filter(fn).map(el =>keys.reduce((acc, key) => {acc[key] =el[key];return acc;}, {}));
const data = [
{
id: 1,
name: 'john',
age: 24
},
{
id: 2,
name: 'mike',
age: 50
}
];
let a = reducedFilter(data, ['id', 'name'], item => item.age > 24); // [{ id: 2, name: 'mike'}]
转换驼峰拼写的字符串为特定格式。
使用 String.replace() 去除下划线,连字符和空格,并将驼峰拼写格式的单词转换为全小写。省略第二个参数 separator ,默认使用 _ 分隔符。
const fromCamelCase = (str, separator = '_') =>str.replace(/([a-z\d])([A-Z])/g, '$1' + separator + '$2').replace(/([A-Z]+)([A-Z][a-z\d]+)/g, '$1' + separator + '$2').toLowerCase();
fromCamelCase('someDatabaseFieldName', ' '); // 'some database field name'
fromCamelCase('someLabelThatNeedsToBeCamelized', '-'); // 'some-label-that-needs-to-be-camelized'
fromCamelCase('someJavascriptProperty', '_'); // 'some_javascript_property'
const isAbsoluteURL = str => /^[a-z][a-z0-9+.-]*:/.test(str);
isAbsoluteURL('https://google.com'); // true
isAbsoluteURL('ftp://www.myserver.net'); // true
isAbsoluteURL('/foo/bar'); // false
const getDaysDiffBetweenDates = (dateInitial, dateFinal) => (dateFinal - dateInitial) / (1000 * 3600 * 24);
getDaysDiffBetweenDates(new Date('2017-12-13'), new Date('2017-12-22')); // 9
const deDupe = (myArray) => [... new Set(myArray)];
deDupe([1, 1, 2, 1, 3, 3, 4])
// [1, 2, 3, 4]
const uniqueElementsBy = (arr, fn) =>arr.reduce((acc, v) => {if (!acc.some(x => fn(v, x))) acc.push(v);return acc;}, []);
uniqueElementsBy([{id: 1, name: 'Jhon'}, {id: 2, name: 'sss'}, {id: 1, name: 'Jhon'}], (a, b) => a.id == b.id)
// [{id: 1, name: 'Jhon'}, {id: 2, name: 'sss'}]
const RGBToHex = (r, g, b) => ((r << 16) + (g << 8) + b).toString(16).padStart(6, '0');
RGBToHex(255, 165, 1); // 'ffa501'
const passwordReg = /(?!^(\d+|[a-zA-Z]+|[~!@#$%^&*?]+)$)^[\w~!@#$%^&*?]{8,20}$/;
// -长度8~20位字符,支持大小写字母、数字、符号三种字符中任意两种字符的组合
const hasClass = (el, className) => new RegExp(`(^|\\s)${className}(\\s|$)`).test(el.className);
如果有好的意见和建议,可以在该issue下提出,比较合理的意见会采纳后加入到上方意见列表
先占坑
追加:
https://github.com/intrinsiclabs/osgood
https://github.com/laverdet/isolated-vm
MinorNote:
setTimeout系列,console,以及其他常用系列在nodejs,vm中是没有的。
Figma提供的web解决方案
https://www.figma.com/blog/how-we-built-the-figma-plugin-system/
Module | Secure | Memory Limits | Isolated | Multithreaded | Module Support | Inspector Support |
---|---|---|---|---|---|---|
vm | ✅ | ✅ | ||||
worker_threads | ✅ | ✅ | ✅ | |||
vm2 | ✅ | ✅ | ✅ | |||
napajs | ✅ | ✅ | Partial | |||
webworker-threads | ✅ | ✅ | ||||
tiny-worker | ✅ | ✅ | ||||
isolated-vm | ✅ | ✅ | ✅ | ✅ | ✅ |
手机号:mobile
/^1((3[\d])|(4[5,6,9])|(5[0-3,5-9])|(6[5-7])|(7[0-8])|(8[1-3,5-8])|(9[1,8,9]))\d{8}$/
国内座机电话(如: 010-12345678):telephone
/\d{3}-\d{8}|\d{4}-\d{7}/
电话或手机:phoneNo
/(^1([3|4|5|7|8|])\d{9}$)|(^(\(\d{3,4}\)|\d{3,4}-|\s)?\d{7,14}$)/
email地址:email
/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/
身份证:IDcard
/^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$|^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/
账号4-10位数字或字母组成
/^[0-9A-Za-z]{4,10}$/
账号由5-16位数字字母下划线组成
/^[a-zA-Z][a-zA-Z0-9_]{4,15}$/
账号4到16位(字母,数字,下划线,减号)
/^[a-zA-Z0-9_-]{4,16}$/
是否字母:isAlpha
/^[a-zA-Z]*$/
非字母
/[^A-Za-z]/
大写字母组成
/^[A-Z]+$/
小写字母组成
/^[a-z]+$/
数字或字母或汉字:numAlphaCn
/^[0-9a-zA-Z\u4E00-\uFA29]*$/
中文和数字组成
/^(([\u4E00-\u9FA5])|(\d))+$/
数字和字母组成
/^[A-Za-z0-9]+$/
是否为中文
/^[\u4E00-\u9FA5]/
整数:int
/^-?\d+$/
域名
[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(/.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+/.?
网址
[a-zA-z]+://[^\s]* 或 ^http://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$
整数且大于0:intAndThanZero
/^([1-9]\d*(\.\d+)?|0)$/
小数
/^\d+\.\d+$/
8位纯数字
/^[0-9]{8}$/
正整数、小数或0
/^\d+(\.?|(\.\d+)?)$/
上传图片类型
/image\/(png|jpg|jpeg|gif)$/
日期格式,如: 2000-01-01
/^\d{4}(-)\d{1,2}\1\d{1,2}$/
一年的12个月(01~09和1~12):
/^(0?[1-9]|1[0-2])$/
一个月的31天(01~09和1~31):
/^((0?[1-9])|((1|2)[0-9])|30|31)$/
是否html标签
/<(.*)>.*<\/\1>|<(.*) \/>/
是否qq号格式
/^[1-9]*[1-9][0-9]*$/
密码强度正则,最少6位,包括至少1个大写字母,1个小写字母,1个数字,1个特殊字符
/^.*(?=.{6,})(?=.*\d)(?=.*[A-Z])(?=.*[a-z])(?=.*[!@#$%^&*? ]).*$/
ipv4地址正则
/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
16进制颜色
/^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/
6至20位,以字母开头,字母,数字,减号,下划线(微信号)
/^[a-zA-Z]([-_a-zA-Z0-9]{5,19})+$/
首尾空白字符
^\s*|\s*$或(^\s*)|(\s*$)
邮政编码:postCode
/^(0[1-7]|1[0-356]|2[0-7]|3[0-6]|4[0-7]|5[1-7]|6[1-7]|7[0-5]|8[013-6])\d{4}$/
添加或者修改对象属性
Object.defineProperty(对象,属性名称,,属性描述符)
通过此方法添加的属性,默认:
Vue 数据双向绑定就是用数据劫持 + 订阅者-发布者模式,数据劫持即使用 Object.defineProperty(),为属性提供 getter/setter 方法,在操作数据时,同步视图,发布消息给订阅者。
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.