passive

在 JavaScript 中,passive 是一个布尔值,用于指定事件监听器是否应该被标记为“被动”的。这个属性主要用于触摸事件(如 touchstarttouchmovetouchendtouchcancel)和滚轮事件(如 wheel)。

什么是被动事件监听器?

被动事件监听器是一种不会阻止浏览器默认行为的异步事件监听器。默认情况下,事件监听器是同步的,这意味着它们会阻止浏览器执行默认行为,直到事件处理程序执行完毕。但是,被动事件监听器不会阻止浏览器执行默认行为,从而提高了滚动性能。

如何使用 passive 选项?

你可以通过在事件监听器的 addEventListener 方法中设置 passive 选项来指定事件监听器是否应该被标记为被动。

1
2
3
4
5
6
7
element.addEventListener(
"touchstart",
function (event) {
// 处理触摸事件
},
{ passive: true }
);

在上面的例子中,我们为 touchstart 事件添加了一个被动的事件监听器。这意味着浏览器可以自由地执行默认的触摸行为,而不会等待事件处理程序执行完毕。

注意事项

  • 被动事件监听器只能用于触摸事件和滚轮事件。对于其他事件,passive 选项将被忽略。
  • 如果事件处理程序需要阻止默认行为,那么它不能被标记为被动。否则,浏览器将无法执行默认行为。
  • 被动事件监听器可以提高滚动性能,但它们可能会影响其他事件的处理。因此,在使用被动事件监听器时,要确保你的代码可以正确地处理所有事件。

示例

以下是一个使用被动事件监听器的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html>
<head>
<title>Passive Event Listener Example</title>
</head>
<body>
<div id="scrollable" style="width: 200px; height: 200px; overflow: scroll;">
<div style="height: 1000px;"></div>
</div>

<script>
const scrollable = document.getElementById("scrollable");

scrollable.addEventListener(
"scroll",
function (event) {
console.log("Scrolling...");
},
{ passive: true }
);
</script>
</body>
</html>

在上面的例子中,我们为 scroll 事件添加了一个被动的事件监听器。这意味着浏览器可以自由地执行滚动行为,而不会等待事件处理程序执行完毕。

apply、bind、call对比

在 JavaScript 中,applybindcall 是用于改变函数执行上下文(即 this 关键字)的方法。它们在函数调用时非常有用。下面分别介绍这三个方法,并给出手写实现的代码。

1. apply

apply 方法调用一个函数,并显式地指定 this 的值和参数列表(以数组或类数组对象的形式)。

1
2
3
4
5
6
7
function greet(greeting, punctuation) {
console.log(greeting + ", " + this.name + punctuation);
}

const person = { name: "Alice" };

greet.apply(person, ["Hello", "!"]); // 输出: Hello, Alice!

2. call

call 方法与 apply 类似,也是调用一个函数,并显式地指定 this 的值和参数列表(以逗号分隔的形式)。

1
2
3
4
5
6
7
function greet(greeting, punctuation) {
console.log(greeting + ", " + this.name + punctuation);
}

const person = { name: "Alice" };

greet.call(person, "Hello", "!"); // 输出: Hello, Alice!

3. bind

bind 方法创建一个新的函数,当这个新函数被调用时,其 this 值会被指定为 bind 方法的第一个参数,其余参数将作为新函数的参数。

1
2
3
4
5
6
7
8
function greet(greeting, punctuation) {
console.log(greeting + ", " + this.name + punctuation);
}

const person = { name: "Alice" };

const greetPerson = greet.bind(person);
greetPerson("Hello", "!"); // 输出: Hello, Alice!

手写实现

下面是 applycallbind 的手写实现:

apply

1
2
3
4
5
6
7
8
9
10
11
12
13
Function.prototype.myApply = function (context = window, args = []) {
if (typeof this !== "function") {
throw new TypeError(this + " is not a function");
}

const fnSymbol = Symbol();
context[fnSymbol] = this;

const result = context[fnSymbol](...args);

delete context[fnSymbol];
return result;
};

call

1
2
3
4
5
6
7
8
9
10
11
12
13
Function.prototype.myCall = function (context = window, ...args) {
if (typeof this !== "function") {
throw new TypeError(this + " is not a function");
}

const fnSymbol = Symbol();
context[fnSymbol] = this;

const result = context[fnSymbol](...args);

delete context[fnSymbol];
return result;
};

bind

1
2
3
4
5
6
7
8
9
10
11
Function.prototype.myBind = function (context, ...args) {
if (typeof this !== "function") {
throw new TypeError(this + " is not a function");
}

const self = this;

return function (...newArgs) {
return self.apply(context, args.concat(newArgs));
};
};

示例

1
2
3
4
5
6
7
8
9
10
11
function greet(greeting, punctuation) {
console.log(greeting + ", " + this.name + punctuation);
}

const person = { name: "Alice" };

greet.myApply(person, ["Hello", "!"]); // 输出: Hello, Alice!
greet.myCall(person, "Hello", "!"); // 输出: Hello, Alice!

const greetPerson = greet.myBind(person);
greetPerson("Hello", "!"); // 输出: Hello, Alice!

对比

方法 是否调用 参数形式
apply 数组或类数组对象
call 逗号分隔的参数列表
bind 逗号分隔的参数列表

通过这些手写实现,可以更好地理解 applycallbind 的内部工作原理。

CommonJS中的exports、module.exports、和this

在 CommonJS 模块系统中,exportsmodule.exportsthis 是三个非常重要的对象,它们用于导出模块中的变量、函数或类。下面分别介绍这三个对象,并通过示例代码进行说明。

1. exports

exports 是一个对象,用于将模块中的变量、函数或类导出。默认情况下,exportsmodule.exports 的一个引用。

2. module.exports

module.exports 是一个对象,用于显式地导出模块中的内容。与 exports 不同的是,module.exports 可以被赋值为任何类型的值,包括对象、函数、类等。

3. this

在模块的上下文中,this 通常指向 exports 对象。这意味着你可以使用 this 来导出模块中的内容。

示例代码

假设我们有以下两个文件:example.jsmain.js

example.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// example.js
this.a = 1;
exports.b = 2;
exports = {
c: 3,
};
mudule.exports = {
d: 4,
};
exports.e = 5;
this.f = 6;
// ---当前结果---
// this {a:1,b:2,f:6}
// exports {c:3,e:5}
// module.exports {d:4}

main.js

1
2
3
// main.js
const example = require("./example");
console.log(example); // {d:4}

在上面的 main.js 文件中,我们分别调用了 example.js 中通过 exportsmodule.exportsthis 导出的函数。

总结

  • exports 是一个对象,用于将模块中的内容导出。
  • module.exports 可以被赋值为任何类型的值,用于显式地导出模块中的内容。(require 引入得到的东西)
  • this 通常指向 exports 对象,可以用于快捷地导出模块中的内容。

在实际开发中,推荐使用 module.exports 来导出模块,因为它提供了更大的灵活性。

ReactPHP实现简单的scoket服务器

首先,确保已经安装了 ReactPHP 库:

1
composer require react/react

然后,创建一个 PHP 文件,比如socket_server.php,并添加以下代码:

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
<?php

require 'vendor/autoload.php';

use React\Socket\Server;
use React\Socket\ConnectionInterface;
use React\EventLoop\Factory as LoopFactory;

// 创建事件循环
$loop = LoopFactory::create();

// 创建TCP服务器
$server = new Server('127.0.0.1:8080', $loop);

// 当有新的连接时
$server->on('connection', function (ConnectionInterface $conn) {
echo "New connection from {$conn->getRemoteAddress()}\n";

// 当接收到数据时
$conn->on('data', function ($data) use ($conn) {
echo "Received data: $data\n";

// 回显数据给客户端
$conn->write("Echo: $data\n");
});

// 当连接关闭时
$conn->on('close', function () use ($conn) {
echo "Connection closed from {$conn->getRemoteAddress()}\n";
});
});

echo "Server running at tcp://127.0.0.1:8080\n";

// 运行事件循环
$loop->run();

这个脚本创建了一个 TCP 服务器,监听在127.0.0.1:8080。当有新的连接时,它会打印出连接的客户端地址,并在接收到数据时回显数据给客户端。当连接关闭时,它会打印出连接关闭的消息。

要运行这个服务器,只需在命令行中执行:

1
php socket_server.php

然后,你可以使用 telnet 或其他 Socket 客户端工具连接到这个服务器,比如:

1
telnet 127.0.0.1 8080

连接成功后,你可以输入一些文本,服务器会回显你输入的内容。