数量关系公式

行程问题基本行程

核心公式

路程 = 速度 × 时间

推导公式

路程的比例 = 速度的比例 × 时间的比例

① 时间相同时,路程之比等于速度之比

② 速度相同时,路程之比等于时间之比

③ 路程相同时,时间之比等于速度反比

等近距离平均速度公式

v 的平均速度 = ( 2 v1 v2 ) / ( v1 + v2 )

火车过桥公式

桥长 + 车长 = 火车速度 × 过桥时间

过桥时间 = ( 桥长 + 列车长 ) ÷ 速度

速度 = ( 桥长 + 列车长 ) ÷ 过桥时间

相遇追及问题公式

线型单次相遇追及公式

相遇距离 s = ( v1 + v2 ) × 相遇时间 t

追及距离 s = ( v1 – v2 ) × 追及时间 t

线型多次相遇公式

单边型多次相遇路程和 2ns = ( v1 + v2 ) × 相遇时间 t

两边型多次相遇路程和 ( 2n – 1 ) s = ( v1 + v2 ) × 相遇时间 t

(n 为相遇次数,s 为两地距离)

环形多次相遇追及公式

环形相遇 ns = ( v1 + v2 ) × 反向运动时间

环形追及 ns = ( v1 + v2 ) × 同向运动时间

(n 为相遇次数,s 为两地距离)

流水行船

流水行船问题

顺流航程 = ( 船速 + 水速 ) × 顺流时间

逆流航程 = ( 船速 – 水速 ) × 逆流时间

电梯运动问题

电梯级数 = ( 人速 + 电梯速度 ) × 沿电梯运动方向到达时间

电梯级数 = ( 人速 – 电梯速度 ) × 逆电梯运动方向到达时间

ECMAScript 6 Note

ES 6 又称 ECMAScript 6 / ECMAScript 2015 / ES 2015

更新了以下特性:

1. let、const 和 block 作用域

let 允许创建块级作用域,ES6 推荐在函数中使用 let 定义变量,而非 var:

var a = 2;
{
  let a = 3;
  console.log(a); // 3
}
console.log(a); // 2

同样在块级作用域有效的另一个变量声明方式是 const,它可以声明一个常量。ES6 中,const 声明的常量类似于指针,它指向某个引用,也就是说这个「常量」并非一成不变的,如:

{
  const ARR = [5,6];
  ARR.push(7);
  console.log(ARR); // [5,6,7]
  ARR = 10; // TypeError
}

有几个点需要注意:

  • let 关键词声明的变量不具备变量提升(hoisting)特性
  • let 和 const 声明只在最靠近的一个块中(花括号内)有效
  • 当使用常量 const 声明时,请使用大写变量,如:CAPITAL_CASING
  • const 在声明时必须被赋值

2. 箭头函数(Arrow Functions)

ES6 中,箭头函数就是函数的一种简写形式,使用括号包裹参数,跟随一个 =>,紧接着是函数体:

var getPrice = function() {
  return 4.55;
};
 
// Implementation with Arrow Function
var getPrice = () => 4.55;

需要注意的是,上面例子中的 getPrice 箭头函数采用了简洁函数体,它不需要 return 语句,下面这个例子使用的是正常函数体:

let arr = ['apple', 'banana', 'orange'];
 
let breakfast = arr.map(fruit => {
  return fruit + 's';
});
 
console.log(breakfast); // apples bananas oranges

当然,箭头函数不仅仅是让代码变得简洁,函数中 this 总是绑定总是指向对象自身。具体可以看看下面几个例子:

function Person() {
  this.age = 0;
 
  setInterval(function growUp() {
    // 在非严格模式下,growUp() 函数的 this 指向 window 对象
    this.age++;
  }, 1000);
}
var person = new Person();

我们经常需要使用一个变量来保存 this,然后在 growUp 函数中引用:

function Person() {
  var self = this;
  self.age = 0;
 
  setInterval(function growUp() {
    self.age++;
  }, 1000);
}

而使用箭头函数可以省却这个麻烦:

function Person(){
  this.age = 0;
 
  setInterval(() => {
    // |this| 指向 person 对象
    this.age++;
  }, 1000);
}
 
var person = new Person();

3. 函数参数默认值

ES6 中允许你对函数参数设置默认值:

let getFinalPrice = (price, tax=0.7) => price + price * tax;
getFinalPrice(500); // 850

4. Spread / Rest 操作符

Spread / Rest 操作符指的是 …,具体是 Spread 还是 Rest 需要看上下文语境。

当被用于迭代器中时,它是一个 Spread 操作符:

function foo(x,y,z) {
  console.log(x,y,z);
}
 
let arr = [1,2,3];
foo(...arr); // 1 2 3

当被用于函数传参时,是一个 Rest 操作符:

function foo(...args) {
  console.log(args);
}
foo( 1, 2, 3, 4, 5); // [1, 2, 3, 4, 5]

5. 对象词法扩展

ES6 允许声明在对象字面量时使用简写语法,来初始化属性变量和函数的定义方法,并且允许在对象属性中进行计算操作:

function getCar(make, model, value) {
  return {
    // 简写变量
    make,  // 等同于 make: make
    model, // 等同于 model: model
    value, // 等同于 value: value
 
    // 属性可以使用表达式计算值
    ['make' + make]: true,
 
    // 忽略 `function` 关键词简写对象函数
    depreciate() {
      this.value -= 2500;
    }
  };
}
 
let car = getCar('Barret', 'Lee', 40000);
 
// output: {
//     make: 'Barret',
//     model:'Lee',
//     value: 40000,
//     makeBarret: true,
//     depreciate: [Function: depreciate]
// }

6. 二进制和八进制字面量

ES6 支持二进制和八进制的字面量,通过在数字前面添加 0o 或者0O 即可将其转换为八进制值:

let oValue = 0o10;
console.log(oValue); // 8
 
let bValue = 0b10; // 二进制使用 `0b` 或者 `0B`
console.log(bValue); // 2

7. 对象和数组解构

解构可以避免在对象赋值时产生中间变量:

function foo() {
  return [1,2,3];
}
let arr = foo(); // [1,2,3]
 
let [a, b, c] = foo();
console.log(a, b, c); // 1 2 3
 
function bar() {
  return {
    x: 4,
    y: 5,
    z: 6
  };
}
let {x: x, y: y, z: z} = bar();
console.log(x, y, z); // 4 5 6

8. 对象超类

ES6 允许在对象中使用 super 方法:

var parent = {
  foo() {
    console.log("Hello from the Parent");
  }
}
 
var child = {
  foo() {
    super.foo();
    console.log("Hello from the Child");
  }
}
 
Object.setPrototypeOf(child, parent);
child.foo(); // Hello from the Parent
             // Hello from the Child

9. 模板语法和分隔符

ES6 中有一种十分简洁的方法组装一堆字符串和变量。

  • ${ … } 用来渲染一个变量
  • ` 作为分隔符
let user = 'Barret';
console.log(`Hi ${user}!`); // Hi Barret!

10. for…of VS for…in

for…of 用于遍历一个迭代器,如数组:

let nicknames = ['di', 'boo', 'punkeye'];
nicknames.size = 3;
for (let nickname of nicknames) {
  console.log(nickname);
}
// 结果: di, boo, punkeye

for…in 用来遍历对象中的属性:

let nicknames = ['di', 'boo', 'punkeye'];
nicknames.size = 3;
for (let nickname in nicknames) {
  console.log(nickname);
}
Result: 0, 1, 2, size

11. Map 和 WeakMap

ES6 中两种新的数据结构集:Map 和 WeakMap。事实上每个对象都可以看作是一个 Map。

一个对象由多个 key-val 对构成,在 Map 中,任何类型都可以作为对象的 key,如:

var myMap = new Map();
 
var keyString = "a string",
    keyObj = {},
    keyFunc = function () {};
 
// 设置值
myMap.set(keyString, "value 与 'a string' 关联");
myMap.set(keyObj, "value 与 keyObj 关联");
myMap.set(keyFunc, "value 与 keyFunc 关联");
 
myMap.size; // 3
 
// 获取值
myMap.get(keyString);    // "value 与 'a string' 关联"
myMap.get(keyObj);       // "value 与 keyObj 关联"
myMap.get(keyFunc);      // "value 与 keyFunc 关联"

WeakMap

WeakMap 就是一个 Map,只不过它的所有 key 都是弱引用,意思就是 WeakMap 中的东西垃圾回收时不考虑,使用它不用担心内存泄漏问题。

另一个需要注意的点是,WeakMap 的所有 key 必须是对象。它只有四个方法 delete(key),has(key),get(key) 和set(key, val):

let w = new WeakMap();
w.set('a', 'b'); 
// Uncaught TypeError: Invalid value used as weak map key
 
var o1 = {},
    o2 = function(){},
    o3 = window;
 
w.set(o1, 37);
w.set(o2, "azerty");
w.set(o3, undefined);
 
w.get(o3); // undefined, because that is the set value
 
w.has(o1); // true
w.delete(o1);
w.has(o1); // false

12. Set 和 WeakSet

Set 对象是一组不重复的值,重复的值将被忽略,值类型可以是原始类型和引用类型:

let mySet = new Set([1, 1, 2, 2, 3, 3]);
mySet.size; // 3
mySet.has(1); // true
mySet.add('strings');
mySet.add({ a: 1, b:2 });

可以通过 forEach 和 for…of 来遍历 Set 对象:

mySet.forEach((item) => {
  console.log(item);
    // 1
    // 2
    // 3
    // 'strings'
    // Object { a: 1, b: 2 }
});
 
for (let value of mySet) {
  console.log(value);
    // 1
    // 2
    // 3
    // 'strings'
    // Object { a: 1, b: 2 }
}

Set 同样有 delete() 和 clear() 方法。

WeakSet

类似于 WeakMap,WeakSet 对象可以让你在一个集合中保存对象的弱引用,在 WeakSet 中的对象只允许出现一次:

var ws = new WeakSet();
var obj = {};
var foo = {};
 
ws.add(window);
ws.add(obj);
 
ws.has(window); // true
ws.has(foo);    // false, foo 没有添加成功
 
ws.delete(window); // 从结合中删除 window 对象
ws.has(window);    // false, window 对象已经被删除

13. 类

ES6 中有 class 语法。值得注意是,这里的 class 不是新的对象继承模型,它只是原型链的语法糖表现形式。

函数中使用 static 关键词定义构造函数的的方法和属性:

class Task {
  constructor() {
    console.log("task instantiated!");
  }
 
  showId() {
    console.log(23);
  }
 
  static loadAll() {
    console.log("Loading all tasks..");
  }
}
 
console.log(typeof Task); // function
let task = new Task(); // "task instantiated!"
task.showId(); // 23
Task.loadAll(); // "Loading all tasks.."

类中的继承和超集:

class Car {
  constructor() {
    console.log("Creating a new car");
  }
}
 
class Porsche extends Car {
  constructor() {
    super();
    console.log("Creating Porsche");
  }
}
 
let c = new Porsche();
// Creating a new car
// Creating Porsche

extends 允许一个子类继承父类,需要注意的是,子类的constructor 函数中需要执行 super() 函数。

当然,你也可以在子类方法中调用父类的方法,如super.parentMethodName()。

在 这里 阅读更多关于类的介绍。

有几点值得注意的是:

  • 类的声明不会提升(hoisting),如果你要使用某个 Class,那你必须在使用之前定义它,否则会抛出一个 ReferenceError 的错误
  • 在类中定义函数不需要使用 function 关键词

14. Symbol

Symbol 是一种新的数据类型,它的值是唯一的,不可变的。ES6 中提出 symbol 的目的是为了生成一个唯一的标识符,不过你访问不到这个标识符:

var sym = Symbol( "some optional description" );
console.log(typeof sym); // symbol

注意,这里 Symbol 前面不能使用 new 操作符。

如果它被用作一个对象的属性,那么这个属性会是不可枚举的:

var o = {
    val: 10,
    [ Symbol("random") ]: "I'm a symbol",
};
 
console.log(Object.getOwnPropertyNames(o)); // val

如果要获取对象 symbol 属性,需要使用Object.getOwnPropertySymbols(o)。

15. 迭代器(Iterators)

迭代器允许每次访问数据集合的一个元素,当指针指向数据集合最后一个元素时,迭代器便会退出。它提供了 next() 函数来遍历一个序列,这个方法返回一个包含 done 和 value 属性的对象。

ES6 中可以通过 Symbol.iterator 给对象设置默认的遍历器,无论什么时候对象需要被遍历,执行它的 @@iterator 方法便可以返回一个用于获取值的迭代器。

数组默认就是一个迭代器:

var arr = [11,12,13];
var itr = arr[Symbol.iterator]();
 
itr.next(); // { value: 11, done: false }
itr.next(); // { value: 12, done: false }
itr.next(); // { value: 13, done: false }
 
itr.next(); // { value: undefined, done: true }

你可以通过 [Symbol.iterator]() 自定义一个对象的迭代器。

16. Generators

Generator 函数是 ES6 的新特性,它允许一个函数返回的可遍历对象生成多个值。

在使用中你会看到 * 语法和一个新的关键词 yield:

function *infiniteNumbers() {
  var n = 1;
  while (true){
    yield n++;
  }
}
 
var numbers = infiniteNumbers(); // returns an iterable object
 
numbers.next(); // { value: 1, done: false }
numbers.next(); // { value: 2, done: false }
numbers.next(); // { value: 3, done: false }

每次执行 yield 时,返回的值变为迭代器的下一个值。

17. Promises

ES6 对 Promise 有了原生的支持,一个 Promise 是一个等待被异步执行的对象,当它执行完成后,其状态会变成 resolved 或者rejected。

var p = new Promise(function(resolve, reject) {  
  if (/* condition */) {
    // fulfilled successfully
    resolve(/* value */);  
  } else {
    // error, rejected
    reject(/* reason */);  
  }
});

每一个 Promise 都有一个 .then 方法,这个方法接受两个参数,第一个是处理 resolved 状态的回调,一个是处理 rejected 状态的回调:

p.then((val) => console.log("Promise Resolved", val),
       (err) => console.log("Promise Rejected", err));

https://www.barretlee.com/blog/2016/07/09/a-kickstarter-guide-to-writing-es6/

ECMAScript 6 扫盲——小胡子哥的个人

https://es6.ruanyifeng.com/

ECMAScript 6 入门——阮一峰

WebStorm 配置 TypeScript

WebStorm 可以开发 TypeScript,同时支持自动编译为 JavaScript 文件,具体操作如下:

1. 前往 Node.js 官网下载安装 Node.js

2. 下载安装新版本的 WebStorm

3. 使用 node 的 npm 命令安装 TypeScript 编译器

npm install typescript -g

安装好,在命令行可以查看编译器的版本:

tsc --version

4. 打开 WebStorm,新建一个空白项目

5. 在 WebStorm 中右击项目名,选择 new -> tsconfig.json File,创建 tsconfig.json 文件

6. 打开 WebStorm,为 TypeScript 文件更改编译设置,File -> Settings -> File Watchers -> TypeScript,这里我们需要选择 TypeScript,但是 File Watchers 下默认是不存在的。需要点击右侧“ + ”号,选择 <custom>

7. 在弹出的 New File Watcher 中,填写 Name,在 Wached Files 中的 File type 选择 TypeScriptWatcher Settings 中填写如下内容:

Program(程序):tsc 的路径

Arguments(参数):--sourcemap --target "ES6"

Output paths to refresh(要刷新的输出路径):$FileNameWithoutExtension$.js:$FileNameWithoutExtension$.js.map

Working directory(工作目录):$FileDir$

8. 同时,需要设置 TypeScript 自动编译,在 Setting -> Language & Frameworks 中的 TypeScript 勾选 Compiler 中的 Recomplie on changes

9. 之后修改 TypeScript 文件时,WebStorm 就会自动编译

CSS 中的长度单位

CSS 中的长度有很多种,有很多没接触过,这次就来记录一次各单位的介绍。

px

1px 表示屏幕上一个像素的大小,该长度是绝对的。这是最传统的长度表示方法,用 px 表示长度的好处就是准确而直接。但是由于现在屏幕的尺寸越来越多,各种尺寸差别很大,使用 px 表示长度很难获得统一的视觉效果,在排版上也经常需要去肉眼估,一些宽度需要手动不断调整。

em

em 这个单位稍微有点绕,1em 的大小等于当前元素上 font-size 的大小。该长度是相对的。在 body 上默认的 font-size 是 16px,而子元素默认继承父元素的 font-size。也就是说在整个页面都没有规定 font-size 时,全局 1em = 16px。

有时为简化 font-size 的换算,需要在 css 中的 body 选择器中声明 font-size = 62.5%,此时 1em = 16px * 62.5% = 10px,这样 12px = 1.2em,10px = 1em,也就是说换算时只需将原有 px 数值除以 10,然后换上 em 作为单位即可。

这里需要注意的是当你在不同的地方设定了不同的 font-size 的话 1em 的值也会变得不同,尤其是 font-size 属性会从父元素继承,当你用 em 来设置 font-size 的值时,还需考虑父元素 font-size 的大小。

以下这种情况在 html 上 font-size 为 10px,div 内 font-size 首先从父元素继承 10px 然后再 1.2 = 12px,于是在 div 内的当前元素的其他地方如设置宽度高度时 1em = 12px,同理在 p 里面 font-size 为 12p * 1.2 = 14.4px,于是在 div 内的当前元素的其他地方设置长度时1em = 14.4px。

由于 em 这个单位跟字体是挂钩的,所以当我们在文字排版方面用 em 就会比较方便,比如:菜单、表格等。当我们知道一列表格最长显示 4 个字,那么我们就可以把宽度设置为 4em。此外只要改变字体的大小就能很轻易的调整所有元素的大小。

rem

理解了 em 之后,rem 就简单多了,rem 表示相当于根元素的 em 值,它避免像上面例子那样字体大小逐层复合的连锁反应导致长度计算的复杂性。确定了 body 的 font-size 值之后,rem 可以当做绝对长度一样使用。在不规定 body 的 font-size 的默认情况,1rem = 16px。目前除 IE 8 及更早版本外,所有浏览器均已支持 rem。

%

% 也是一个相对长度单位,100% 等于父元素的宽度或高度(不包括 padding )。对于当前元素的宽度、左右 100% 等于父元素的宽度,而对于高度、上下 100% 等于父元素的高度。

vm / vh

vw / vh 是一种视窗单位,也是相对单位。它相对的不是父节点或者页面的根节点。而是由视窗(Viewport)大小来决定的,1vw 等于视窗宽度的 1%,而 1vh 等于视窗高度的 1%。

视窗(Viewport)是你的浏览器实际显示内容的区域,换句话说是你的不包括工具栏和按钮的网页浏览器。

vw / vh 优势在于能够直接获取高度,而用 % 在没有设置 body 高度的情况下,是无法正确获得可视区域的高度的,所以这是挺不错的优势。

vmin / vmax

vmin 表示当前 vw 和 vh 中较小的一个值 vmax 表示当前 vw 和 vh 中较大的一个值。

做移动页面开发时,如果使用 vw / wh 设置字体大小(比如 5vw ),在竖屏和横屏状态下显示的字体大小是不一样的。

由于 vmin 和 vmax 是当前较小的 vw 和 vh 和当前较大的 vw 和 vh。这里就可以用到 vmin 和 vmax。使得文字大小在横竖屏下保持一致。

vw / vh,vmin / vmax:IE 9+ 局部支持,Google Chrome / Firefox / Safari / Opera 支持,iOS Safari 8+ 支持,Android Browser 4.4+ 支持,Google Chrome for android 39 支持。

其他

单位描述
in英寸
cm厘米
mm毫米
ex一个 ex 是一个字体的 x-height(x-height 通常是字体尺寸的一半)。
pt磅(1pt 等于 1/72 英寸)
pc12 点活字(1 pc 等于 12 点)


他们都是绝对长度单位有以下关系:

1 in = 2.54 cm = 25.4 mm = 101.6 q = 72 pt = 6 pc = 96 px

如何获取访问端 IP 地址的方法

如果使用 nginx 作为 web 代理服务器,则将添加 proxy_set_header X-Real-IP $remote_addr; 到站点的位置块。例如:

server {
    listen 443 ssl http2;
    server_name server_name; 

    client_max_body_size 1024m;

    location / {
        proxy_redirect off;
        proxy_set_header HOST $http_host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-NginX-Proxy true;
        proxy_pass IP address;
    }
}

并且,如果采用 Express 作为后端开发框架,可以使用 req.headers["x-real-ip"] || req.connection.remoteAddress 获取 IP 地址。

此方法仅限简单使用,具体使用完善获取访问端 IP 地址可使用第三方中间件,如:express-iprequest-ip 等。

使用 Python 进行简单的人脸识别

通过使用 Python 进行简单的人脸识别非常简单,使用到了 OpenCV 库内的 haarcascade_frontalface_default.xml ,haarcascade 包含经过训练的用于检测对象的特定类型的分类器,例如:人脸,人眼,倾斜的人脸,鼻子,口嘴,微笑,上半身,下半身等。

代码

import cv2
import os


def load_img(path, name, mun=30, add_with=0):
    if not os.path.exists(path):
        os.mkdir(path)
    # 获取人脸识别模型
    classfier = cv2.CascadeClassifier('haarcascade_frontalface_default.xml文件路径')
    # 创建一个窗口
    cv2.namedWindow('Face')
    # 使用内置摄像头
    cap = cv2.VideoCapture(0)
    # 使用视频进行识别
    # cap = cv2.VideoCapture('视频路径')
    # 根据摄像头设置IP及rtsp端口
    # url='rtsp://admin:admin@192.168.0.104:8554/live'
    # cap = cv2.VideoCapture(url)
    i = 0  # 计数
    if cap.isOpened():
        while i < mun:
            ok, frame = cap.read()  # 读取一帧图片
            if not ok:
                continue

            faces = classfier.detectMultiScale(frame, 1.3, 5, minSize=(32, 32))

            if len(faces) > 0:
                for face in faces:
                    x, y, w, h = face

                    img = frame[y - add_with:y + h + add_with, x - add_with:x + w + add_with]
                    # 显示人脸框
                    cv2.rectangle(frame, (x - add_with, y - add_with), (x + w + add_with, y + h + add_with),
                                  (0, 255, 0), 2)
                    save_path = path + '/' + name + '_' + str(i) + '.jpg'
                    print(save_path)
                    img2 = cv2.resize(img, (112, 112))
                    cv2.imwrite(save_path, img2)
                    i += 1

            cv2.imshow('face', frame)
            c = cv2.waitKey(10)
            if c & 0xFF == ord('q'):
                break

        cap.release()
        cv2.destroyAllWindows()


if __name__ == '__main__':
    # 第一个参数为保存图片的路径
    # 第二个参数为保存图片名字的开头
    # 第三个参数为图片的数量
    # 第四个参数可以调节图片的大小
    load_img('保存图片文件夹路径', 'lq', 500, 0)

Git 删除历史记录,清理仓库

当 commit 次数很多的时候,项目仓库会慢慢积攒很多无用的提交历史,这些历史记录会在项目 clone 的时候导致速度很慢。如果没有软件,一些 clone 过程体验会很不好,甚至频繁出错,所以删除提交历史可以作为一种清理仓库的手段。

Git 代码

git checkout --orphan latest_branch
git add -A
git commit -am "init commit"
git branch -D master
git branch -m master
git push -f origin master

以下是各条语句的解析

git checkout --orphan latest_branch
# 创建新分支 "latest_branch" 并转到该分支
# git checkout 命令为在当前分支上创建新分支(并转到该分支)
# git checkout --orphan 命令为从空白处创建新分支(并转到该分支)

git add -A
# 添加所有文件(当前最新状态)到暂存区

git commit -am "commit message"
# 将暂存区提交到新纪录(latest_branch分支)
# "commit message"为新纪录的名称

git branch -D master
# 删除主分支

git branch -m master
# 将 latest_branch(当前) 分支重命名为 master(主分支)

git push -f origin master
# 同步本地状态到服务器

# 此时,完成的效果为:
#   将master分支所有记录删除,并新建一个 "commit message" 记录,记录中内容为当前项目最新状态
#   此时远程服务器内所有垃圾都被清空
#   如果使用了 Github Desktop 等软件,本地垃圾可能未被清空,可删除项目并重新 clone

Element Plus Table(表格)点击获取对应 id

网上搜到的内容或许适用于 vue 2 或 Element UI,不适用 vue 3 和 Element Plus。

template:

<el-table-column label="配置" width="120" align="center">
  <template v-slot="scope">
    <el-button link type="primary" size="small">编辑</el-button>
    <el-button link type="primary" @click="deleteNavigation(scope.row.id)" size="small">删除
    </el-button>
  </template>
</el-table-column>

script:

function deleteNavigation(id) {
  console.log(id);
}

使用 vue 的 v-slot 指令,这个指令中是 vue 2.6 新增的,取代了以前的 slot 和 slot-scope,而我使用的是 vue 3,所以网上搜到的很多使用 slot-scope 的方法都失效了。

使用 插槽名.row 可以获取一行的内容,如果要获取行内的某一个字段的值,只需要再点出它的字段名,在我的代码里,就是 scope.row.id

如果要获取行的索引,使用 插槽名.$index,注意$符号是必不可少的,否则将不起作用。

MySQL 子用户建库仅访问自身库

有若干个 MySQL 用户可以使用以下命令:CREATE DATABASE name; 但我希望这些用户中的每一个人都能够只查看和访问他们自己创建的数据库。

GRANT ALL PRIVILEGES ON user_%. * TO 'user'@'%';

问题是用户在命名数据库时必须非常小心。

例如:如果用户 admin 创建数据库 root_test,则用户 root 可以独占访问该数据库,而用户 admin 则不能。

[] == false 和 !![] == true 解析

==是相等操作符,先转换再比较;===是全等操作符,仅比较不转换。

[] == false 解析

右侧布尔值很简单,直接转换成 0

[] 不是基本数据类型,它是对象,所以需要调用 valueOf 方法(Array.prototype.valueOf() ,但是 Array 没有实现 valueOf 方法,所以根据原型链,最终调用的是 Object.prototype.valueOf):

[].valueOf():得到的仍然是 [] ,继续调用 toString 方法(Array.prototype.toString()):

[].toString():得到""

""作为基本数据类型直接调用 Number("") 得到 0

所以最终结果是 true


!![] == true

!!== 的优先级更高,所以先看 !![]

![] 得到 false (操作数是对象的话逻辑非返回 false),然后再取非,得到 true

很明显,true == true 得到的结果是 true