每日一问---es2020
ES2020新增方法
类的私有属性
通过在变量或函数前面添加一个简单的散列符号,我们可以将它们完全保留在类内部使用。
静态方法
类相当于实例的原型,所有在类中定义的方法,都会被实例继承。
如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,
而是直接通过类来调用,这就称为“静态方法”。静态属性
静态属性指的是 Class 本身的属性,即Class.propName,
而不是定义在实例对象(this)上的属性。私有属性 私有方法
私有方法和私有属性,是只能在类的内部访问的方法和属性,外部不能访问。
class InnerStatic {
// 静态属性
static staticProps = 'string';
// 静态方法
static staticFun = ()=>{};
// 私有属性
#message = 1;
// 私有方法
#getMessage(){
console.log('#message values', this.#message);
}
实例方法
getMessage(){
}
}
let instance = new InnerStatic();
instance.getMessage();
console.log(instance.#message) // Private name #message is not defined无效合并操作符
因为 JavaScript 是动态类型的,所以在分配变量时需要牢记 JavaScript 对真 / 假值的处理。 如果我们有一个具有一些值的对象,有时候我们希望允许一些技术上有误的值,比如一个空字符串或者数字0。 设置默认值很快会变得烦人,因为它会覆盖应该是有效值的内容。
let person = {
profile: {
name: '',
age: 0
}
};
console.log('无效合并操作符', person.profile.name || 'Anonymous'); // Anonymous
console.log('无效合并操作符', person.profile.name ?? 'Anonymous'); // ''可选的链接操作符
在点符号之前添加一个问号,我们可以使值的路径的任何部分可选
let person = {};
// console.log(person.profile.name ?? ''); // Cannot read property 'name' of undefined
console.log(person?.profile?.name); // undefined
if (person?.profile?.name) {
console.log('have name props');
} else {
console.log('not exits name props');
}
可用于判断数据属性是否存在,不必写成冗长的
person && person.profile && person.profile.name的格式BigInt
通过在末尾加上字母 n 解决js中数字超过最大数值精度错误问题
const max = Number.MAX_SAFE_INTEGER;
console.log(max); // 9007199254740991
console.log(max + 1);
console.log(max + 2);
console.log(max + 3);
const bigNum = 1000000000000000000000000000000000000000000000n;
console.log(bigNum + 1n); // 1000000000000000000000000000000000000000000001nDynamic Import 动态导入
如果您的文件中充满了实用函数,其中一些函数可能很少被使用,导入它们的所有依赖项可能只是浪费资源。 现在我们可以使用 async / await 在需要的时候动态导入依赖关系。
math.js
const add = (num1, num2) => num1 + num2;
export { add };
---------------------
index.js
const doMath = async (num1, num2) => {
if (num1 && num2) {
const math = await import('./math.js');
console.log(math.add(5, 10));
};
};
doMath(4, 2);Promise.allSettled
接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束。
const p1 = new Promise((res, rej) => setTimeout(res, 1000));
const p2 = new Promise((res, rej) => setTimeout(rej, 1000));
Promise.allSettled([p1, p2]).then(data => console.log(data));
// [
// Object { status: "fulfilled", value: undefined},
// Object { status: "rejected", reason: undefined}
// ]参考资料
每日一问---React生命周期
React生命周期
React版本 16.13.1生命周期
constructor(props)
componentWillMount() ===> UNSAFE_componentWillMount()
render()
componentDidMount()
componentWillReceiveProps(nextProps) ===> UNSAFE_componentWillReceiveProps
shouldComponentUpdate(nextProps, nextState)
componentWillUpdate(nextProps, nextState) ===> UNSAFE_componentWillUpdate
componentDidUpdate(prevProps, prevState, snapshot)
componentWillUnmount()
componentDidCatch新的生命周期函数
static getDerivedStateFromProps(nextProps, prevState)
static getDerivedStateFromError()
getSnapshotBeforeUpdate()
constructor(props)
在 React 组件挂载之前,会调用它的构造函数。在这里可以进行state的初
始化操作和为事件处理函数绑定实例componentWillMount()
在挂载之前被调用。它在 render() 之前调用,因此在此方法中同步调用 setState() 不会触发额外渲染
此方法是服务端渲染唯一会调用的生命周期函数
React 16.3版本中引入UNSAFE_componentWillMount别名
React 16.X版本中采用componentWillMount()会产生警告警告
React 17.0版本删除 componentWillMount()render()
render() 方法是 class 组件中唯一必须实现的方法。
通过createElement方法返回对应类型的react渲染数据对象
render() 函数应该为纯函数,不能够有副作用
componentDidMount()
在组件挂载后(插入 DOM 树中)立即调用,在此处可以进行一些AJXA、DOM获取、订阅等副作用的操作
componentWillReceiveProps(nextProps)
在已挂载的组件接收新的 props 之前被调用
在挂载过程中不会调用,在组件props改变时候才会触发,内部this.state的改变不会触发componentWillReceiveProps()
React 16.3版本中引入UNSAFE_componentWillReceiveProps别名
React 16.X版本中采用componentWillReceiveProps()会产生警告警告
React 17.0版本删除 componentWillReceiveProps()shouldComponentUpdate(nextProps, nextState)
当 props 或 state 发生变化时,shouldComponentUpdate 会在渲染执行之前被调用。返回值默认为 true。首次渲染或使用 forceUpdate() 时不会调用该方法。
根据 shouldComponentUpdate 的返回值,判断 React 组件的输出是否受当前 state 或 props 更改的影响。返回false则当下更改不会影响组件
返回 false 并不会阻止子组件在 state 更改时重新渲染
目前版本 如果 shouldComponentUpdate 返回 false,则不会调用UNSAFE_componentWillUpdate,render 和 componentDidUpdate
在此中可以做是否渲染的优化操作
componentWillUpdate(nextProps, nextState)
当组件收到新的 props 或 state 时,会在渲染之前调用 componentWillUpdate。使用此作为在更新发生之前执行准备更新的机会。初始渲染不会调用此方法。
React 16.3版本中引入UNSAFE_componentWillUpdate别名
React 16.X版本中采用 componentWillUpdate() 会产生警告警告
React 17.0版本删除 componentWillUpdate()componentDidUpdate(prevProps, prevState, snapshot)
在更新后会被立即调用。首次渲染不会执行此方法。
componentWillUnmount()
在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作,例如,清除 timer,取消网络请求或清除在 componentDidMount() 中创建的订阅等。
componentDidCatch(error, info)
此生命周期在后代组件抛出错误后被调用。
其接受两个参数
1.error 抛出的错误
2.info 带有 componentStack key 的对象,其中包含有关组件引发错误的栈信息。getDerivedStateFromProps(props,state)
getDerivedStateFromProps 会在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。它应返回一个对象来更新 state,如果返回 null 则不更新任何内容。
static getDerivedStateFromProps(props, state)此方法可看做componentWillMount和componentWillReceiveProps的结合,但是有一些区别
1.在此静态方法中无权访问组件实例
2.需要返回一个对象更新state
3.每次渲染前触发此方法,componentWillReceiveProps仅在父组件重新渲染触发
getSnapshotBeforeUpdate(prevProps,PrevState)
getSnapshotBeforeUpdate() 在最近一次渲染输出(提交到 DOM 节点)之前调用。它使得组件能在发生更改之前从 DOM中捕获一些信息(例如,滚动位置)。此生命周期的任何返回值将作为参数传递给 componentDidUpdate()。
getDerivedStateFromError(error)
此生命周期会在后代组件抛出错误后被调用。它将抛出的错误作为参数,并返回一个值以更新 state
static getDerivedStateFromError(error) {
// 更新 state 使下一次渲染可以显降级 UI
return { hasError: true };
}生命周期执行流程
参考资料
每日一问---前端安全CSRF
关于前端安全CSRF
跨站请求伪造(英語:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。
1.攻击原理
假设用户登录到不受保护的某银行网站,并且使用会话 cookie 或 JWT cookie 进行身份验证,并进行转账操作
假如其转账操作的URL地址如下:
http://www.examplebank.com/withdraw?account=AccoutName&amount=1000&for=PayeeName攻击者欺骗用户执行脚本(通过点击垃圾邮件链接或类似)
用户进入第三方网站
<img src="http://www.examplebank.com/withdraw?account=Alice&amount=1000&for=Badman由于不受保护的某银行网站的登录信息未过期,服务器将成功地验证用户身份并执行交易
在CSRF中攻击者并不直接获取用户信息,其只是欺骗用户浏览器,让其以用户的名义进行操作
2.攻击流程
3.预防策略
1.检查Referer字段
HTTP头中有一个Referer字段,这个字段用以标明请求来源于哪个地址。

在处理敏感数据请求时,通常来说,Referer字段应和请求的地址位于同一域名下。
以上面银行操作为例,
Referer字段地址通常应该是转帐按钮所在的网页地址,应该也位于www.examplebank.com之下。而如果是CSRF攻击传来的请求,Referer字段会是包含恶意网址的地址,
不会位于www.examplebank.com之下,这时候服务器就能识别出恶意的访问。但存在恶意篡改其Referer字段的可能
2.token验证
既然攻击者利用第三方cookie的方式去获取用户信息,并验证用户身份这种方式进行攻击,
可以采取增加一个独立的token进行认证,
在用户登录信任网站的时候,服务端生成一个唯一token并给到用户,
用户每次和服务端通信,都会校验token的有效性,
如果校验token失败,则认为可能是 CSRF 攻击而拒绝该请求。参考资料
每日一问---关于前端安全XSS
关于前端安全XSS
XSS
XSS,即 Cross Site Script,中译是跨站脚本攻击;其原本缩写是CSS,但为了和层叠样式表(Cascading Style Sheet)有所区分,因而在安全领域叫做 XSS
攻击者通过恶意插入脚本,实现对用户访问网站内容的控制
其大致分为三种类型
1.反射型(Reflected XSS)
当服务器端脚本立即使用 web 客户端提供的数据(最常见的是 HTTP 查询参数(例如 HTML 表单提交))来解析并向用户显示结果页面时,这些漏洞就会显现出来
反射 XSS 是攻击者发现页面有这样的漏洞时发生的攻击,例如:
假如页面的HTML是如下
<body>
...
<div> showing results for keyword
<script>
document.write(
window.location.href.substr(window.location.href.indexOf('q=') + 2)
)
</script>
</div>
...
...JavaScript results...
...
</body>在此输入URL地址
期望的 URL: https://mywebpage.com/search?q=javascript如果恶意注入代码如下
恶意URL: https://mywebpage.com/search?q=<script>恶意代码</script>当攻击者诱使用户点击这种恶意的 URL ,用户敏感数据会被泄露
攻击的流程如下

2.存储型(Stored XSS)
攻击者提供的数据被服务器保存,然后在正常浏览过程中返回给其他用户的“正常”页面上永久显示,而没有适当的 HTML 转义。
其攻击流程如下

3.基于DOM型(DOM based XSS)
恶意代码通过 JavaScript 代码完全在客户端反映出来的
这种攻击和反射型是类似的,但不同的是,恶意的 URL 部分将不会被发送到服务器。 例如:
期望 URL: https://mywebpage.com/search?q=javascript基于DOM型
恶意 URL: https://mywebpage.com/search#q=<script>恶意代码</script>如果在页面中存在
var text = document.getElementById("text");
text.innerHTML = malicious URL就会破坏DOM结构,攻击者就可以通过脚本实现控制内容
如何防护
每一个可以由用户输入并在您的应用程序中使用的值(无论是在服务器端还是在客户端)都应该被视为不受信任的数据,因此应该在使用前进行处理!
输入检测
对用户输入的内容进行检测并转义
function escapeText(text) {
return text.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
}输出检查
在服务端数据渲染HTML的时候,使用编码或转义的方式避免
参考资料
XSS CSRF 例子
github book
<<白帽子讲 Web 安全>>
每日一问---React中render函数
React中关于render函数返回一个元素
问题需求
当在render返回的方法中,需要返回多个根节点时候会如何关于Jsx
由于react jsx是javascript的语法拓展,其本质是 通过React.createElement(type, config, children)来返回一个React元素
class App extends React.Component {
render(){
return (
<div>
hello
</div>
);
}
}转变这样
_createClass(App, [{
key: 'render',
value: function render() {
return React.createElement("div", null, "hello");
}
}]);如果写成多个元素
class App extends React.Component {
render(){
return (
<div>
hello
</div>
<h1>more</h1>
);
}
}其将会装换为
_createClass(App, [{
key: 'render',
value: function render() {
return React.createElement("div", null, "hello");
return React.createElement("h1", null, "more");
}
}]);其实际上不会转换成功(将会报错 Adjacent JSX elements must be wrapped in an enclosing tag)其方法return两次,这显然是错误
解决方法
1.父元素包裹
render() {
return (
<div>
<h1>foo</h1>
<h2>bar</h2>
</div>
);
}2.采用 Fragment
在 React v16.2+以上版本中可以才用Fragment片段的方式
render() {
return (
<React.Fragment>
<td>Hello</td>
<td>World</td>
</React.Fragment>
);
} 3.短语法 <> </>
render() {
return (
<>
<td>Hello</td>
<td>World</td>
</>
);
}
jsx部分 其转换为
React.createElement(React.Fragment, null,
React.createElement("td", null, "Hello"),
React.createElement("td", null, "World")
); 可以看出 其实际上是React.Fragment的语法糖 可以写成
render() {
return (
<React.Fragment>
<td>Hello</td>
<td>World</td>
</React.Fragment>
);
}4.返回数组
在 React v16.0+以上版本中可以才用返回数组的方式
render(){
return [<div key={0}>hello</div>,<div key={1}>world</div>]
}其会转化为
return [
React.createElement("div", {key: 0}, "hello"),
React.createElement("div", {key: 1}, "world")
];参考资料
每日一问---Class类中this绑定
class类中this绑定
类的方法内部如果含有this,默认指向类的实例,但是一些场景下使用,会导致this的指向偏离预期,所以需要绑定定义时候的this,也就是实例对象
如下
<!--html-->
<button id="btn">click</button>
<!--js-->
const btn = document.getElementById('btn');
class CheckThis {
constructor(ele) {
this.ele = ele;
this.count = 0;
this.ele.addEventListener('click', this.click);
}
click() {
console.log(this); // DOM
this.count++;
console.log(this.count); // NaN
}
}
const thisInstance = new CheckThis(btn);在这里click方法中的this指的是dom节点而不是实例对象,
因为addEventListener()将this的指针设置为冒泡事件的DOM元素
相当于
var m = this.click;
this.ele.addEventListener('click', m) //此时m函数中this的指向创建事件的元素再看一个例子
import React, { PureComponent } from 'react';
class Index extends PureComponent {
testHandle(){
console.log(this); // undefined
};
reder(){
return (
<Button onClick={this.testHandle}>click</Button>
)
}
}React事件为合成事件,class并不会为方法自动绑定this到当前对象上,再由于class 内部是严格模式,所以 this 实际指向的是undefined,为避免这种this指向问题有几种方法来解决这种问题
1.使用箭头函数
直接在React元素中采用箭头函数
1 | import React, { PureComponent } from 'react'; |
因为箭头函数中的this指向的是函数定义的对象,所以可以保证this总是指向当前组件的实例对象
优化点:直接在render方法中为元素事件定义事件的处理函数,每次render调用时,都会创建一个新的事件处理函数,带来额外的性能开销
2.组件方法
直接将组件的方法赋值给元素的事件属性,同时在类的构造函数中,将这个方法的this绑定到当前对象。
1 | import React, { PureComponent } from 'react'; |
这种方式的好处是每次render不会重新创建一个回调函数,没有额外的性能损失。但在构造函数中,为事件处理函数绑定this, 尤其是存在多个事件处理函数需要绑定时,这种模板式的代码还是会显得烦琐。而且对于函数传递参数来说不太方便
1 | import React, { PureComponent } from 'react'; |
这种方式依然存在每次render都会创建一个函数的问题,但在需要为处理传递额外参数提供便利
3.属性初始化语法
使用ES 7的property initializers会自动为class中定义的方法绑定this
1 | import React, { PureComponent } from 'react'; |
这种方式既不需要在构造函数中手动绑定this, 也不需要担心组件重复渲染导致的函数重复创建问题。但是, property initializers这个特性还处于试验阶段, 默认是不支持的,不过, 使用官方脚手架Create React App创建的项目默认是支持这个特性的。你也可以自行在项目中引入babel的transform-class-properties插件获取这个特性支持。
参考资料
引用 React+进阶之路