JavaScript的事件委托、判断URL是否合法及全排列

一、JavaScript的事件委托

在JavaScript中,事件委托可以让我们在父元素上监听事件,而不是在每个子元素上单独监听事件。这样做的好处是可以减少事件绑定的数量,提高性能。

事件委托的原理是利用事件冒泡,将子元素的事件冒泡到父元素上。通过判断触发事件的目标元素,来执行相应的操作。

1. 绑定事件

在父元素上绑定事件,比如在一个ul元素上监听所有li元素的点击事件:

let ulElement = document.querySelector('ul');

ulElement.addEventListener('click', function(event) {

if (event.target.tagName === 'LI') {

console.log('clicked on li element');

}

});

上面的代码中,我们在ul元素上监听click事件,然后在回调函数中判断触发事件的目标元素是不是li元素。

2. 优点

事件委托的优点有:

减少事件绑定的数量,提高性能;

可以动态添加子元素,仍然能监听到事件。

3. 缺点

事件委托的缺点有:

如果父元素上绑定了太多事件,会影响性能;

对于不需要委托的子元素,还是会冒泡到父元素上,造成不必要的操作。

二、判断URL是否合法

在开发Web应用时,我们经常需要检测URL是否合法,可以使用正则表达式来实现。一个合法的URL通常包括以下部分:

协议(http或https);

域名(www.example.com);

端口号(可选);

路径(可选);

查询参数(可选);

锚点(可选)。

1. 使用正则表达式判断URL是否合法

下面是一个使用正则表达式判断URL是否合法的例子:

function isUrlValid(url) {

const regex = /^(https?|ftp):\/\/[^\s/$.?#].[^\s]*$/i;

return regex.test(url);

}

console.log(isUrlValid('http://www.example.com')); // true

console.log(isUrlValid('https://www.example.com/path/to/file.html?q1=v1&q2=v2#anchor')); // true

console.log(isUrlValid('www.example.com')); // false

console.log(isUrlValid('example.com')); // false

上面的代码中使用了一个正则表达式来匹配URL。一个URL应该以http、https或ftp开头,后面跟着://,然后是任意非空白字符和非空格字符。如果有端口号,可以使用冒号加数字表示。路径、查询参数和锚点可以使用/、?、#来分隔。

2. 各部分详解

下面是对正则表达式的详细解释:

^(https?|ftp):// // 必须以http、https或ftp开头

[^\s/$.?#].[^\s]*$ // 中间部分,不能包含空格和特殊字符,可以包含-

(:\d+)? // 可选的端口号

(/[^\s]*)* // 可选的路径

(\?[\w&=]*)? // 可选的查询参数

(#.*)? // 可选的锚点

三、全排列

在计算机科学中,全排列是对一组数据进行排列的所有可能的方式。对于长度为n的序列,全排列的个数为n!(n的阶乘)。

1. 递归实现

function permute(input) {

let result = [];

function permuteHelper(input, temp) {

if (input.length === 0) {

result.push(temp.slice());

} else {

for (let i = 0; i < input.length; i++) {

let elem = input.splice(i, 1)[0];

temp.push(elem);

permuteHelper(input, temp);

input.splice(i, 0, elem);

temp.pop();

}

}

}

permuteHelper(input, []);

return result;

}

console.log(permute([1,2,3])); // [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

上面的代码通过递归实现全排列。递归函数permuteHelper接收两个参数:输入数组input和中间结果数组temp。在递归开始时,temp为空,input包含了所有要排列的元素。每次从input中选取一个元素加入temp中,然后递归地求解剩下的元素的全排列。递归结束时,将temp加入结果数组result,然后返回。

2. 非递归实现

下面是非递归实现全排列的代码:

function permute(input) {

let result = [];

let n = input.length;

let p = new Array(n).fill(0);

let i = 1;

let j;

result.push(input.slice());

while (i < n) {

if (p[i] < i) {

j = i % 2 && p[i];

[input[i], input[j]] = [input[j], input[i]];

result.push(input.slice());

p[i]++;

i = 1;

} else {

p[i] = 0;

i++;

}

}

return result;

}

console.log(permute([1,2,3])); // [[1,2,3],[2,1,3],[3,1,2],[1,3,2],[2,3,1],[3,2,1]]

上面的代码中,我们维护了一个记录每个元素的位置p数组。初始时,输入数组input作为第一个排列的结果,加入结果数组result。然后每次确定input中一个元素的位置,直到所有元素都确定。假设现在要确定第i个元素的位置,如果p[i]小于i,则将输入数组中第i个元素和第p[i]个元素交换位置,然后加入结果数组result。因为p[i]表示前i-1个元素已经全部交换过位置了,p[i]小于i意味着第i个元素还没有和前面的元素交换过位置。然后将p[i]加1,i设为1,继续确定下一个元素的位置。如果p[i]等于i,则说明第i个元素已经和前面所有元素都交换过位置了,将p[i]设为0,i加1,继续确定下一个元素的位置。

3. 性能比较

递归实现和非递归实现的时间复杂度都是 O(n!),但是非递归实现要比递归实现快得多,因为递归实现存在大量的函数调用和堆栈操作。非递归实现只需要维护一个数组,不需要额外的函数调用和堆栈操作,因此性能更好。

免责声明:本文来自互联网,本站所有信息(包括但不限于文字、视频、音频、数据及图表),不保证该信息的准确性、真实性、完整性、有效性、及时性、原创性等,版权归属于原作者,如无意侵犯媒体或个人知识产权,请来电或致函告之,本站将在第一时间处理。猿码集站发布此文目的在于促进信息交流,此文观点与本站立场无关,不承担任何责任。