useBreakPointHook

useBreakpoint Hook —在React中获取媒体查询断点

怎么测量窗口宽度?

要获取断点,我们需要测量浏览器的宽度,然后在window对象上获取该宽度。window对象具有两个宽度:outerWidthinnerWidth,这里我们使用innerWidth

window.innerWidth每当我们调整窗口大小时,我们都会进行测量。

window.addEventListener('resize',dosomethingWithWidth)

因此,每当我们调整窗口大小时,resize都会触发该事件并计算innerWidth,但是实现这样的事情存在一个小问题,每当我们重新调整窗口大小时,都会触发回调函数, 回影响运行性能。因此这里我们可以使用节流函数来减少触发频率。关于节流函数详情请看节流函数与防抖函数.

计算Breakpoint

基于常见设备类型的Breakpoint

  • 如果宽度小于320像素,则该设备会特别小,以表示xs
  • 如果宽度等于或大于320像素且小于720像素,则该设备很小,由表示sm
  • 如果宽度等于或大于720像素且小于1024像素,则该设备为中等,由表示md
  • 如果宽度等于或大于1024px,则该设备很大,由表示lg

useBreakpoint Hook

现在连接React中的所有零碎部分,以获得完整的useBreakpoint自定义Hook。

首先,我们将使用useState挂钩创建状态,该状态将存储我们当前的设备类型。

1
2
3
4
5
6
7
8
9
10
11
12
const  getDeviceBp  =  (width) =>  {
if(width < 320 ) {
return 'xs'
} else if (width > = 320 && width < 720 ) {
return “ sm”
} else if (width > = 720 && width < 1024 ) {
return 'md'
} else if (width > = 1024 ) {
return “ lg”
}
}
const [brkPnt,setBrkPnt] = useState(()=> getDeviceConfig(window.innerWidth))

其次,我们使用useEffect来监听变化

1
2
3
useEffect(()=> {
window.addEventListener('resize',throttle(setBrkPnt(getDeviceBp(window.innerWidth))))
})

最后,为了防止每次调用hook时就注册监听事件,并且在组件卸载时候注销我们我们需要从useEffect回调函数中返回一个函数,该回调函数将在我们的组件被卸载时执行,并将所有逻辑放入其中

1
2
3
4
5
useEffect(()=> {
const calInnerWidth = throttle(setBrkPnt(getDeviceBp(window.innerWidth)));
window.addEventListener('resize',calInnerWidth)
return ()=> window.removeEventListener('resize',calInnerWidth)
},[])

集合

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
import {useState, useEffect} from 'react';
import throttle from './throttle';

const getDeviceBp = (width) => {
if(width < 320 ) {
return 'xs'
} else if (width > = 320 && width < 720 ) {
return “ sm”
} else if (width > = 720 && width < 1024 ) {
return 'md'
} else if (width > = 1024 ) {
return “ lg”
}
}

const useBreakpoint = () => {
const [brkPnt,setBrkPnt] = useState(()=> getDeviceConfig(window.innerWidth))

useEffect(()=> {
const calInnerWidth = throttle(setBrkPnt(getDeviceBp(window.innerWidth)));
window.addEventListener('resize',calInnerWidth)
return ()=> window.removeEventListener('resize',calInnerWidth)
},[])

return brkPnt;
}
export default useBreakpoint;

Digital ocean 部署

Node.js Deployment

Steps to deploy a Node.js app to DigitalOcean using PM2, NGINX as a reverse proxy and an SSL from LetsEncrypt

1. Sign up for Digital Ocean

2. Create a droplet and log in via ssh

I will be using the root user, but would suggest creating a new user

3. Update packages && Install Node/NPM

1
2
3
4
5
6
7
sudo apt update

curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -

sudo apt install nodejs

node --version

4. Clone your project from Github

There are a few ways to get your files on to the server, I would suggest using Git

1
git clone yourproject.git

5. Install dependencies and test app

1
2
3
cd yourproject
npm install
npm start (or whatever your start command)

6. Setup PM2 process manager to keep your app running

1
2
3
4
5
6
7
8
9
10
11
12
13
sudo npm i pm2 -g
pm2 start app (or whatever your file name)

# Other pm2 commands
pm2 show app
pm2 status
pm2 restart app
pm2 stop app
pm2 logs (Show log stream)
pm2 flush (Clear logs)

# To make sure app starts when reboot
pm2 startup ubuntu

You should now be able to access your app using your IP and port. Now we want to setup a firewall blocking that port and setup NGINX as a reverse proxy so we can access it directly using port 80 (http)

7. Setup ufw firewall

1
2
3
4
5
sudo ufw enable
sudo ufw status
sudo ufw allow ssh (Port 22)
sudo ufw allow http (Port 80)
sudo ufw allow https (Port 443)

8. Install NGINX and configure

1
2
3
sudo apt install nginx

sudo nano /etc/nginx/sites-available/default

Add the following to the location part of the server block

1
2
3
4
5
6
7
8
9
10
server_name yourdomain.com www.yourdomain.com;

location / {
proxy_pass http://localhost:5000; #whatever port your app runs on
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
1
2
3
4
5
# Check NGINX config
sudo nginx -t

# Restart NGINX
sudo service nginx restart

You should now be able to visit your IP with no port (port 80) and see your app. Now let’s add a domain

9. Add domain in Digital Ocean

In Digital Ocean, go to networking and add a domain

Add an A record for @ and for www to your droplet

Register and/or setup domain from registrar

Choose “Custom nameservers” and add these 3

  • ns1.digitalocean.com
  • ns2.digitalocean.com
  • ns3.digitalocean.com

It may take a bit to propogate

  1. Add SSL with LetsEncrypt
    1
    2
    3
    4
    5
    6
    7
    sudo add-apt-repository ppa:certbot/certbot
    sudo apt-get update
    sudo apt-get install python-certbot-nginx
    sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

    # Only valid for 90 days, test the renewal process with
    certbot renew --dry-run

Now visit https://yourdomain.com and you should see your Node app

简单本地todo封装

利用localStorage封装todo逻辑

需要注意的是

监听每个item是否被checked逻辑中,如果直接将监听事件绑定到每个item上,会导致新的item在被渲染后未能被成功添加此事件。

当然也可以使用传统方法获取动态DOM节点如getEelementById,但是要通过[``mutationObserver`](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver)不断的获取更新的DOM才能保证新的item也被添加上监听事件。

另一个方法是,可以将点击事件绑定到父元素上,通过e.targe.matches方法得到此item,然后通过获取存储在该item dataset中的索引来改变items对象中的值。

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
class LocalToDo {
constructor(submitForm, itemsWrapper, selectAll, clearAll) {
this.submitForm = submitForm;
this.itemsWrapper = itemsWrapper;
this.selectAll = selectAll;
this.clearAll = clearAll;
this.items = JSON.parse(localStorage.getItem('item')) || [];
}
init() {
this.render(this.itemsWrapper, this.items);
this.submitForm.addEventListener('submit', this.addItem.bind(this));
this.itemsWrapper.addEventListener('click', this.toggleCheckbox.bind(this));
this.selectAll.addEventListener('click', this.toggleSelectAll.bind(this));
this.clearAll.addEventListener('click', this.handleClear.bind(this));
}
addItem(e) {
e.preventDefault();
const text = this.submitForm.querySelector('[name=item]').value;
this.items.push({ text, checked: false });
this.render(this.itemsWrapper, this.items);
localStorage.setItem('item', JSON.stringify(this.items));
this.submitForm.reset();
}
render(itemsWrapper, items = []) {
this.itemsWrapper.innerHTML = this.items
.map(
(item, i) => `
<li>
<input type=checkbox data-index=${i} id=item${i} ${item.checked ? 'checked' : ''} >
<label for=item${i}>${item.text}</label>
</li>
`
)
.join('');
}
toggleCheckbox(e) {
if (!e.target.matches('input')) return;
const index = e.target.dataset.index;
this.items[index].checked = !this.items[index].checked;
localStorage.setItem('item', JSON.stringify(this.items));
}
toggleSelectAll() {
if (this.selectAll.checked) {
this.items.forEach(item => (item.checked = true));
localStorage.setItem('item', JSON.stringify(this.items));
this.render(this.itemsWrapper, this.items);
} else {
this.items.forEach(item => (item.checked = false));
localStorage.setItem('item', JSON.stringify(this.items));
this.render(this.itemsWrapper, this.items);
}
}
handleClear() {
localStorage.removeItem('item');
this.items = [];
this.render(itemsList);
}
}

const addItems = document.querySelector('.add-items');
const itemsList = document.querySelector('.plates');
const selectAll = document.querySelector('#select-all');
const clearAll = document.querySelector('#clear-all');

new LocalToDo(addItems, itemsList, selectAll, clearAll).init();

Next.js For SSR

next.js 与 SSR

术语:

  • 服务器端渲染(SSR):响应每个请求,在服务器上渲染应用程序,然后将水合的HTML和Javascript发送回客户端。

  • 静态渲染(SR):在构建时(例如,当您运行npm run build命令时)渲染应用。通常涉及为每个URL生成一个静态HTML页面。这是NextJS的默认方法。

  • 客户端渲染(CSR):在运行时在浏览器中的客户端上渲染应用程序。

利弊:
即使Next的建议是使用静态渲染,每种方法都有其优点和缺点。

  • 服务器端渲染(SSR):
    优点:

    • 可以动态处理动态路线
    • 渲染页面将始终是最新的

    缺点:

    • 如果要进行繁重的处理可能会很慢,因为客户端在发出请求后必须等待每个页面
    • CDN无法缓存
  • 静态渲染(SR):
    优点:

    • 始终非常快速和高效

    缺点:

    • 如果您不知道所有URL早于(构建)时间,则无法处理动态路由
  • 客户端渲染(CSR):
    缺点:

    • 随着应用程序的增长迅速降低速度,通常应避免使用

NextJS中的SSR,SR和CSR:

getStaticProps —在构建时获取数据
getStaticPaths —在构建时预渲染动态路由
getServerSideProps —在每个请求上获取数据
swr-在运行时从客户端获取数据

基本上,Next.JS将在构建时呈现尽可能多的HTML数据(getStaticProps),然后如果以后需要数据,它将运行getServerSideProps。如果需要在客户端上获取数据,使用swr。

getStaticProps

Next.JS在构建时预先渲染每个页面,并使用 getStaticProps 返回的道具将数据与页面混合。

1
2
const App = ({ data }) => <p>{data}</p>export const getStaticProps = async () => 
({ props: { data: "Some Data" } })

TypeScript Implementation

1
2
3
4
import { GetStaticProps } from 'next'

export const getStaticProps: GetStaticProps = async () =>
({ props: { data: "Some Data" } })

关于getStaticProps的一些重要注意事项:

1.由于它在构建时执行,因此它不会从运行时发出的任何请求接收数据。 其中包括查询参数,HTTP标头等
2.它“仅”运行在“服务器”上,这意味着您可以在此处编写服务器代码,而不会下载到该代码或在“客户端”上运行,例如 API调用,数据库调用,请求等
3.由于它仅在服务器上运行,因此您无需进行本地API调用(例如/ pages / api)。 只需直接在getStaticProps内部编写服务器代码即可。

getStaticPaths

因此,getStaticPaths允许您列出以后希望****需要的动态路由列表,然后在构建时将其渲染(例如,当您运行npm run时) 在您的终端窗口中构建)。 您可以通过返回带有`paths’键(必需)的对象来定义它们。

这就引出了一个问题:“如果** User **请求您未在getStaticPaths中定义的页面会发生什么?”。 返回的对象还具有一个称为“ fallback”的键(必需),该键指示此行为。

如果将“ fallback”关键字设置为“ false”,则所有未定义的路由“ 404”都会退出。

如果将fallback键设置为true,则当User请求未定义页面时,NextJS会抛出在同一文件中定义的fallback HTML,则在后台进行下一步,并使用用户输入的URL完全生成了所请求的页面,然后将其交换为后备HTML。详情参考 docs

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
import { useRouter } from 'next/router'

const Post = ({ post }) => {
const router = useRouter()

if (router.isFallback) {
return <div>Loading...</div>
}

return (
<div>{...post}</div>
)
}

export async function getStaticPaths() {
return {
paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
fallback: true,
}
}

export async function getStaticProps({ params }) {
// if url was /posts/3, params.id will be 3
const res = await fetch(`https://.../${params.id}`)
const post = await res.json()

return { props: { post } }
}

export default Post

TypeScript Implementation

1
2
3
import { GetStaticPaths } from 'next'

export const getStaticPaths: GetStaticPaths = async () => {}

getServerSideProps

NextJS将在每次请求时使用getServerSideProps函数呈现页面**,并将使用该函数返回的数据填充组件props。

根据文档,如果您在构建时无法访问页面所需的数据,则仅应使用getServerSideProps。 即请求中包含一些您需要的信息,以提取所需的数据或呈现页面。

注意,getServerSideProps仅在** Server 上运行,并且永远不会下载到 Client 或在 Client **中运行。

1
2
3
4
const Page = ({ data }) => <div>{data}</div>;export const getServerSideProps = async() => {
const res = await fetch(`https://.../data`);
const data = await res.json(); return { props: { data } };
}export default Page;

TypeScript Implementation

1
2
3
import { GetServerSideProps } from 'next'

export const getServerSideProps: GetServerSideProps = async () => {}

The SWR Hook

Next.JS通常对客户端渲染不满意,但这并不意味着您无法在运行时从客户端获取数据。

Next的聪明人创建了一个名为[SWR](https://swr.vercel.app/)的钩子,该钩子表示**Stale While Revalidate** —反过来又是HTTP缓存失效策略的名称。

我们将在另一篇文章中深入探讨SWR,但与此同时,这里有一个简短的代码片段来演示其用法。

1
2
3
4
5
6
7
8
9
10
import useSWR from 'swr'

const Article = () => {
const { data, error } = useSWR('/api/prices', fetch)

if (error) return <div>There was an error!</div>
if (!data) return <div>Still loading...</div>

return <div>Prices: {data}</div>
}

Conclusion

主要的收获是:尽可能使用静态渲染。 如果您需要信息来渲染仅在用户发出请求时才可用的页面,请使用“服务器端渲染”。 如果您需要实时更新客户端,强烈建议使用SWR。

Form upload

基础概要

同步上传多文件简单实现

1
2
3
4
<form action='server/upload' method='post' enctype='multipart/form-data'>
<input type='file[]' mutiple />
<input type='submit' value='upload'>
</form>

JS 提供的FormData构造函数及其方法

1
2
3
4
5
6
7
8
var newForm = new FormData();
newForm.append('Keyname','value');

newForm.get('Keyname') // 'value'
newForm.has('Keyname') // true

newForm.delete('Keyname')
newForm.get('Keyname') // null

异步上传

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<div>
<input type='file[]' mutiple id='file' />
<input type='submit' value='upload'>
</div>
<div class='progress-bar-wrapper' >
<li class='progress-bar'></li>
<span class="error-info"></span>
</div>

<script>
const fileNode = document.querySelector('#file');
const submitNode = document.querySelector('input[type="submit"]');
const progressBarWrapper = document.querySelector('.progress-bar-wrapper'),

fileNode.addEventListener('onchange',function(){
// files属性可得到一组上传文件的信息,包括文件名称,文件大小(字节),文件类型等等
const files = this.files;
var fileName = '',
fileSize = 0,
maxSize = 1048576,
errorInfo = '',
formData = null;

for(let i=0; i<=files.length; i++){
fileName = files[i].name;
fileSize[i].size;
if(!/\.(gif|jpg|jpeg|png)$/.test(fileName)){
errorInfo = '类型错误'
}
if(fileSize>maxSize){
errorInfo = '文件太大'
}

var progressBar = docuemtn.createElement('li');
progressBar.className = 'progress-bar';
progressBarWrapper.appendChild(progressBar);

if(errorInfo !== ''){
progressBarWrapper.innerHTML = `<span class="error-info">${errorInfo}</span>`
}else{
progressBar.innerHTML = '<div class="progress"></div>'

var formData = new FormData();
formData.append('KeyName',files[i]);
// Ajax
var ajax = window.XMLHttpRequest? new window.XMLHttpRequest() : new ActionXObject('Microsoft.XMLHTTP');
ajax.open('post','server/upload');
// 利用闭包保存i
(function(i){
ajax.upload.onprogress = function(e){
var e = e | window.event,
percent = e.loaded / e.total*100+'%',
thisProgressBar = getElementsByClassName('progress-bar')[i];

thisProgressBar.getElementsByClassName('progress')[0].style.width = perceant;
}
})(i)
ajax.send(formData);
}
}
})

</script>

lazyLoadAndDebounce

如何实现图片懒加载?

思路:

  1. 存储图片地址,可利用DOM中自带的dataset属性
  2. 等用户可视窗口进入图片区域后,通过存储在dataset中的图片地址替换img的src属性
  3. 移除dataset中存储的地址

简单实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
window.addEventListener('scroll', lazyLoad);
const images = document.querySelectorAll('img');
var n = 0;
const lazyLoad = function (e) {
for (var i = n; i < images.length; i++) {
const img = images[i];
// 当图片一半以上暴露在可视窗口,执行下列
const showAt = window.pageYOffset + window.innerHeight - img.height / 2;
const isAtHalf = showAt > img.offsetTop;
if (isAtHalf) {
let src = img.dataset.src;
img.setAttribute('src', src);
img.removeAttribute('data-src');
n++;
}
}

Note: 需要注意的是, 这里利用了变量n来判断图片是否加载完成,避免在滚动时重复执行造成undefined错误。 因为当任意图片再次执行此for循环时,第一次循环已经清除了data-src属性,因此会报undefinded错误。

问题

上述方法中,当用户滚动时,事件监听函数会频繁触发,因此可能会造成不必要的性能损耗,因此就需要防抖函数来限制其触发频率。

 More...

DOM & Event in JavaScript

DOM操作

getElement… 与querySelector的区别

  • 使用getElement…返回的都是动态的集合
  • 使用querySelectorAll返回的是静态集合

获取属性特征

  • 使用特征操作时属性名称不区分大小写
  • 特征值都为字符串类型,如需数值类型需要进行转换
Method Des
getAttribute 获取属性
setAttribute 设置属性
removeAttribute 删除属性
hasAttribute 属性检测

Note: 大部分情况下可以通过属性该更改并可同步到特征集中。但是像input中的value属性需要通过特征方法如setAttribute更改可同步到特征集中。

1
2
3
4
5
6
7
8
9
10
11
12
<input type="text" name="login" value="secret" />
<script>
const login = document.querySelector(`[name='login']`)
login.value = '12345';
console.log(login.getAttribute('value')) // 'secret'
//---------------------------------------------------

login.setAttribute('value', '12345')
console.log(login.value) // '12345'


</script>
 More...

Promise in JavaScript

What is a Promise?

MDN

A Promise is a proxy for a value not necessarily known when the promise is created. It allows you to associate handlers with an asynchronous action’s eventual success value or failure reason.

A Promise is in one of these states:

  • pending: initial state, neither fulfilled nor rejected.

  • fulfilled: meaning that the operation completed successfully.

  • rejected: meaning that the operation failed

Promise chain

A pending promise can either be fulfilled with a value, or rejected with a reason (error). When either of these options happens, the associated handlers queued up by a promise’s then method are called

promise-chain

The methods promise.then(), promise.catch(), and promise.finally() are used to associate further action with a promise that becomes settled.

链式加载

1
2
3
4
5
6
7
8
9
10
11
12
function load(file) {
return new Promise((resolve, reject) => {
const script = document.createElement("script");
script.src = file;
script.onload = () => resolve(script);
script.onerror = () => reject();
document.body.appendChild(script);
});
}
load("js/1.js")
.then(() => load("js/2.js"))
.then(() => <code here>);

使用promise封装定时器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//封装定时器
//定时器为宏任务队列

function timeOut(delay=3000,callback){
return new Promise(resolve=>{
setTimeout(()=>callback(resolve),delay)
//delay to trigger Promise return resolve to callback
})
}

//--------------------------------------------
var timeInterval = (callback, delay = 60) => {
let id = setInterval(() => callback(id), delay)
}

timeInterval((id) => {
let div = document.querySelector("div");
let left = parseInt(window.getComputedStyle(div).left)
div.style.left = left + 10 + 'px';
if (left > 250) {
clearInterval(id)
}
})
 More...

Class in JavaScript

类的本质

类的本质是函数

1
2
3
class User {
};
console.log(typeof User); //function

在类中添加原型方法

1
2
3
4
5
6
7
8
class User {
constructor(name){
this.name = name;
};
getName(){
console.log(this.name);
};
};

相当于:

1
2
3
4
5
6
function User(name){
this.name = name;
};
User.prototype.getName = function(){
console.log(this.name);
}

类与构造函数差异

  • 类定义的方法不能遍历

静态属性和方法只能通过类原型来调用

使用静态方法批量生产对象

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
var data = [{
"1990": 794362.7682,
"code": "GB",
"name": "United Kingdom",
"change": -10,
"region": "EU",
},
{
"1990": 71303.67987,
"code": "SE",
"name": "Sweden",
"change": -20,
"region": "EU",
},
];

class EU {
constructor(data) {
this.model = data;
}
get change() {
return this.model.change;
}
static create(data) {
// data = JSON.parse(JSON.stringify(data));
return data.map(item => new EU(item));
}
static maxChange(data) {
return data.sort((a, b) => {
return Math.abs(b.change) - Math.abs(a.change)
})[0];
}
static totalEmission(data) {
return data.reduce((acc, cur) => {
return acc['1990'] + cur['1990']
})
}
}
var EUs = EU.create(data);

静态属性和方法也是可以被继承的。

访问器

gettersetter 可以用来管理属性,防止属性被随意修改

声明方法:方法前加get or set

1
2
3
4
5
6
7
8
9
10
11
class User {
constructor(){
this.name = name;
};
get name(){
return this.name
};
};
let user1 = new User('ruoyu');
//使用访问器时不需要加括号
console.log(user1.name); // 'ruoyu'

属性保护

  1. 可以通过_<name> 命名方式来告知此属性是私有属性,但此方式只是一种公认的提示,本身并不能防止属性的修改。

  2. 通过symbol来保护属性,比如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    const AGE = Symbol("age")
    class User {
    _name = 'ruoyu';
    constructor(age) {
    this[AGE] = age;
    };
    }
    let user1 = new User(19);
    console.log(user1) // {_name: "ruoyu", Symbol(age): 19}
  3. 通过WeakMap()键值对集

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    const _age = new WeakMap();
    class User {
    constructor(name) {
    this.name = name;
    _age.set(this, {age:18,password:123});
    }
    };
    let user1 = new User('ruoyu');
    //属性并不会暴露在外部,除非使用get访问器
    console.log(md) // {name:'ruoyu'}
    console.log(_age) //WeakMap {User => {age:18,pasword:123}}

Super

super 一直指向当前对象, 在继承关系中,如果想要使用父类中的属性方法,可以使用super关键字来指向当前对象。

下面通过普通方法来模拟super

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let user = {
name: "user",
show() {
return this.name;
}
};
let spawn = {
__proto__: user,
name: "spawn",
show() {
return this.__proto__.show.call(this);
}
};
console.log(spwan.show()); // 'spawn'

super关键词相当于一颗语法糖

1
2
3
4
//上述代码中spwan中的show方法可以简写成:
show(){
return super.show();
}

Note: super关键词只能在类或对象的方法中使用

在继承父类构造者中,可以使用super()方法来调用父类中的构造函数.

1
2
3
4
5
6
7
8
9
10
class Parent {
constructor(name) {
this.name = name;
}
};
class Son extends Parent{
constructor(name){
super(name);
}
}

原理剖析:

1
2
3
4
5
6
7
8
function Parent(name) {
this.name = name;
}
function Son(...args) {
Parent.apply(this, args);
}
Son.prototype = Object.create(Parent.prototype);
Son.prototype.constructor = Son;

Prototype in JavaScript

JavaScript: 所有对象都是对象的实例

MDN:

JavaScript only has one construct: objects.

什么是原型链?

When trying to access a property of an object, the property will not only be sought on the object but on the prototype of the object, the prototype of the prototype, and so on until either a property with a matching name is found or the end of the prototype chain is reached.

prototype vs __proto__

1
2
3
4
( new Foo ).__proto__ === Foo.prototype;
( new Foo ).prototype === undefined;
var b = new Foo(20);
var c = new Foo(30);

Constructor function Foo also has its own __proto__ which is Function.prototype

__proto__ is the actual object that is used in the lookup chain to resolve methods, etc. prototype is the object that is used to build __proto__ when you create an object with new

inherit

__proto__ 不是对象属性,理解为prototypegetter/setter 实现,是一个非标准定义 __proto__ 内部使用getter/setter 控制值,所以只允许对象或null.

 More...
  • © 2020 Ruoyu Wang
  • PV: UV:

请我喝杯咖啡吧~

支付宝
微信