1. 原型和原型链
原型与原型链是JavaScript
面向对象编程的核心概念之一。
首先我们来介绍一下
原型
。
- 在
JavaScript
中,每个对象都有一个原型。 - 原型可以理解为对象的模板或者蓝图,描述了对象应该具有的属性和方法。
- 如果对象本身没有某个属性或方法,
JavaScript
引擎就会在这个对象的原型链上查找,直到找到这个属性或方法为止。 - 换句话说,对象的原型就是该对象的父对象,而引擎在查找属性或方法时会逐级查找父对象的原型,直到原型链的顶端为止。
原型链是由原型组成的一条链,它是
JavaScript
实现继承的重要机制。
当我们调用对象的某个属性或方法时,JavaScript
引擎会首先查找对象本身是否有这个属性或方法。
如果没有,就会在对象的原型上查找。如果原型也不存在该属性或方法,引擎就会在原型的原型上查找,依次类推,直到查找到Object.prototype
为止。
在JavaScript
中,对象和函数都可以拥有原型。
每个函数都有一个prototype
属性,指向该函数的原型对象。
而对象的原型可以通过Object.getPrototypeOf()
方法或者__proto__
属性来获取。
我们可以通过给函数的原型对象添加属性和方法,来让该函数所创建的对象都拥有这些属性和方法。
另外,我们还可以通过修改对象的原型对象来修改该对象的原型链,从而实现继承等高级特性。
下面给出一个简单的案例,演示
对象、构造函数、原型和原型链
的关系:
// 定义一个构造函数 function Person(name, age) { this.name = name; this.age = age; } // 给Person的原型对象添加方法 Person.prototype.sayHello = function() { console.log('Hello, my name is ' + this.name + ', and I am ' + this.age + ' years old.'); }; // 新建一个Person对象 var person = new Person('Tom', 20); // 调用Person对象的方法 person.sayHello(); // 输出:"Hello, my name is Tom, and I am 20 years old." // 修改Person的原型对象 Person.prototype.job = 'programmer'; // 查看person对象的原型链 console.log(person.__proto__); // 输出:Person { job: 'programmer', sayHello: [Function] } console.log(person.__proto__.__proto__); // 输出:Object {}
在上面的例子中,我们通过定义一个构造函数Person
和给它的原型对象添加方法和属性,创建了一个Person
对象,利用原型链实现了方法的继承,并演示了对象的原型和原型链是JavaScript
面向对象编程的核心概念之一。
2. 作用域和闭包
作用域和闭包同样是JavaScript中的核心概念。
作用域指的是代码中变量和函数的可见范围。
JavaScript中有全局作用域和局部作用域两种。在全局作用域中定义的变量和函数在整个程序中都可以访问到,而在局部作用域(函数作用域或块级作用域)中定义的变量和函数只能在该作用域内访问。
变量查找时采用就近原则,即从内到外查找,最终找到全局作用域。
闭包是指有权访问另一个函数作用域中变量的函数。
JavaScript中的函数可以作为值传递,当一个函数被嵌套在另一个函数中时,嵌套的内部函数就可以访问外部函数的变量。这种机制在函数式编程中非常强大,因为它允许我们将函数作为参数传递并且在函数内部访问它们的状态。
闭包是JavaScript强大的函数式编程特性之一。
在实际应用中,作用域和闭包经常结合使用。
通过闭包,我们可以在一个函数中定义变量和函数,并返回一个闭包函数,使得这些变量和函数在闭包函数执行时仍然保存着它们的状态,从而实现JavaScript
中的高阶函数,这是JavaScript
中相当强大的特性之一。
下面是一个简单的示例,演示作用域和闭包的使用:
// 全局作用域 var globalVar = 'global'; function outerFunction() { // 函数作用域 var outerVar = 'outer'; function innerFunction() { // 函数作用域 var innerVar = 'inner'; // 闭包 console.log(innerVar + ' ' + outerVar + ' ' + globalVar); } return innerFunction; } // 返回闭包函数 var closure = outerFunction(); // 执行闭包函数 closure(); // 输出:"inner outer global"
在上面的示例中,我们定义了三个作用域:全局作用域、outerFunction
函数作用域和innerFunction
函数作用域。我们在innerFunction
函数中使用了闭包特性访问了外部函数的变量,实现了变量跨作用域访问的功能。
3. 高阶函数和函数式编程
高阶函数和函数式编程是JavaScript中的高级编程特性,也是JavaScript语言的优势之一。
高阶函数是指可以接收函数作为参数,并且/或者返回一个函数作为结果的函数。它们可以让函数更具有灵活性和可重用性。高阶函数常常会在函数式编程中发挥重要作用。
函数式编程是一种编程范式,它将函数视为一等公民(first-class citizen),强调程序员编写可以接受其他函数作为输入并返回函数作为输出的函数。函数式编程强调作为纯函数的模块化和可组合的设计,试图避免程序状态的可变性和副作用,以提高程序的可读性、可维护性和可重用性。
下面是一个直观的例子,演示了函数式编程和高阶函数的应用:
// 定义一个函数,判断一个数字是否是奇数 function isOdd(num) { return num % 2 !== 0; } // 定义一个高阶函数,接收一个要过滤的数组和一个过滤函数作为参数 function filter(arr, fn) { var result = []; for (var i = 0; i < arr.length; i++) { if (fn(arr[i])) { result.push(arr[i]); } } return result; } // 定义一个数组 var arr = [1, 2, 3, 4, 5]; // 使用高阶函数过滤数组中的奇数 var filteredArr = filter(arr, isOdd); console.log(filteredArr); // 输出:[1, 3, 5]
在上面的例子中,我们定义了一个isOdd
函数,判断一个数字是否是奇数。我们还定义了一个filter
高阶函数,接收一个数组和一个过滤函数作为参数,返回一个满足条件的新数组。最后,我们使用filter
函数过滤了原数组中的奇数,得到新数组[1, 3, 5]。
该例子是函数式编程在JavaScript
中的一个简单应用,以及使用高阶函数可灵活编写可重复使用的代码的一个示例。这些思想可以帮助我们简化复杂的问题,减少错误,并更好地利用计算机的处理能力。
4. 异步编程和Promise、async/await
异步编程是JavaScript
中的重要概念之一。
在JavaScript中,异步编程是指允许程序在等待某些操作完成的同时继续执行其他操作,从而提高程序的性能和响应速度。
JavaScript 中的异步编程通常有三种方式:
- 回调函数
- Promise
- async/await。
Promise 是 ES6 中引入的一种新的异步编程方式,在实践中被广泛应用,它提供了一种更好的方式来组织异步代码。
一个 Promise 表示一个尚未完成的异步操作,它可以有三种状态:
- pending(等待中)
- resolved(完成)
- rejected(异常)
在Promise中可以通过 then() 方法来指定resolved状态的处理程序,通过 catch() 方法来指定rejected状态的处理程序。
以下是一个使用 Promise 的示例,从服务器获取一个 JSON 对象:
fetch('/api/data.json') .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.log(error));
async/await 是ES7 中引入的一种新的异步编程方式,它允许程序在异步操作完成前暂停执行,并使用类似同步代码的方式来处理异步代码。async/await 基于 Promise,通过 async 函数和 await 关键字实现。async/await 能够更简单、更清晰地描述异步代码,避免回调函数地狱。
以下是一个使用 async/await 的示例:
async function getData() { try { const response = await fetch('/api/data.json'); const data = await response.json(); console.log(data); } catch(error) { console.log(error); } } getData();
在上面的代码中,使用 async
关键字定义了一个异步函数 getData
,该函数使用 await
关键字暂停程序的执行,直到获取 JSON
数据。在 try/catch
块中,使用 await
关键字等待异步操作完成,并从服务器获取请求的数据。如果出现错误,将在 catch
块中捕获它。
总之,异步编程和 Promise、async/await 是现代JavaScript
中非常重要的概念和特性,不仅可以提高程序的性能和响应速度,还可以使代码更易于处理异步操作。
5. 正则表达式
正则表达式是一种用于匹配字符串中模式的工具,它可以对字符串进行分割、搜索、替换等操作,是现代计算机语言中不可或缺的一部分。
在 JavaScript
中,正则表达式通过 RegExp
对象来实现,它们可以使用直接量定义(/pattern/)或者通过 RegExp
构造函数来创建。正则表达式采用一种基于特定字符集的语法,可以使用一些特殊字符来表示某些特殊模式,如字符集、量词、分组等等。
以下是一些常用的正则表达式语法:
- 字符集:用 [] 括起来表示一组可能出现的字符。例如,[aeiou] 表示匹配任意一个元音字母。
- 特殊字符集:用特殊参数表示字符集。例如,\d 表示匹配任意数字字符,\w 表示匹配任意单词字符。
- 量词:用 {n,m} 表示出现次数的区间,用 ? 表示可选的0或1次,用 * 表示可选的0或多次,用 + 表示至少出现一次。
- 定位符:用 ^ 表示匹配行首,$ 表示匹配行尾。
- 分组:用 () 表示分组,可以将子表达式分组,以便便捷地处理这些子表达式。
以下是一些示例正则表达式:
- 匹配
Email
地址的正则表达式:/^[a-z0-9]+([-_.][a-z0-9]+)*@[a-z0-9]+([-_.][a-z0-9]+)*\.[a-z]{2,}$/i
- 匹配中国大陆手机号的正则表达式:
/^1[3-9]\d{9}$/
- 匹配
HTML
标签的正则表达式:/<[^>]+>/g
在 JavaScript
中,可以使用正则表达式的函数进行字符串匹配,如 String.prototype.match()
、String.prototype.replace()
、RegExp.prototype.test()
等等。这些函数可以对字符串进行分割、搜索、替换等操作,非常便捷。
正则表达式是JavaScript
中非常重要的工具和语言特性,可以为我们解决许多字符串处理的问题,但它也会带来一些复杂性,因此需要仔细学习和运用。
12道前端知识题目深入浅出下JavaScript(二)https://developer.aliyun.com/article/1426208