此文章,来源于印客学院的资料【第一部分:基础篇(105题)】,这里只是分享,便于查漏补缺。
诸君可以根据自己实际情况,自行衡量,看看哪里需要加强。
概述如下:
闭包就是能够读取其他函数内部变量的函数
闭包是指有权访问另⼀个函数作用域中变量的函数
,创建闭包的最常⻅的方式就是在⼀个函数内创建另⼀个函数, 通过另⼀个函数访问这个函数的局部变量,利用闭包可以突破作用链域
闭包的特性:
说说你对闭包的理解
使用闭包主要是为了设计私有的方法和变量。闭包的优点是可以避免全局变量的污染
, 缺点是闭包会常驻内存,会增大内存使用量,使用不当很容易造成内存泄露
。在js中, 函数即闭包, 只有函数才会产生作用域的概念
闭包的最大用处有几个,
读取函数内部的变量
,让这些变量始终保 持在内存中
好处:能够实现封装和缓存等;
坏处:就是消耗内存 、不正当使用会造成内存溢出的问题
使用闭包的注意点
使用闭包的示例
当内部函数在外部函数返回后仍然可以访问外部函数的变量时,就形成了闭包。
下面是一个经典的 JavaScript 闭包示例:
function outerFunction() {
var x = 10;
function innerFunction() {
console.log(x); // 内部函数访问外部函数的变量 x
}
return innerFunction; // 返回内部函数的引用
}
var closure = outerFunction(); // 调用外部函数并将返回的内部函数保存在 closure 变量中
closure(); // 输出 10,内部函数仍然可以访问外部函数的变量 x
在这个示例中,outerFunction()
是外部函数,它有一个变量 x
和一个内部函数 innerFunction()
。内部函数可以访问外部函数的变量 x
,即使外部函数已经执行完毕。outerFunction()
返回了内部函数的引用,并将其保存在 closure
变量中。然后,我们通过调用 closure()
来执行内部函数,输出变量 x
的值,即 10。
闭包是 JavaScript 中强大且常用的概念。通过使用闭包,我们可以实现许多有趣和有用的功能,比如创建私有变量、封装特定逻辑等。
更多精彩内容,请微信搜索“前端爱好者
“, 戳我 查看 。
保证执⾏环境里有权访问的变量和函数是有序的
,作用域链的变量只能向上访问
, 变量访问到 window 对象即被终止
,作用域链向下访问变量是不被允许的作用域链是 JavaScript 中用于查找变量和函数的机制
。它是由多个执行环境(也称为作用域)形成的嵌套结构,每个作用域都有一个关联的变量对象或词法环境。
当在代码中引用一个变量时,JavaScript 引擎会按照作用域链从内到外依次查找该变量。作用域链的构建方式是根据变量的声明位置确定的。
在函数中,每个函数都有自己的作用域,在函数创建时会创建一个特殊的内部对象,我们称之为词法环境。词法环境包含了函数的所有局部变量,函数的参数以及对外层作用域的引用。当函数执行时,它会在自己的词法环境中查找变量,并在需要时逐级向外层作用域查找,直到找到变量或者达到全局作用域。
如果变量在当前作用域中找不到,JavaScript 引擎会继续沿着作用域链向外层作用域查找,直到达到全局作用域。全局作用域是所有作用域的最外层作用域,它包含了所有全局变量和函数,以及一些内置的 JavaScript 对象和函数。
作用域链的构建过程发生在函数定义时。当一个函数被定义时,它的作用域链就被确定了,它会包含函数自身的词法环境以及外层作用域的引用。
总之,作用域链是 JavaScript 中用于查找变量和函数的一种机制,它由多个嵌套的作用域构成,根据变量的声明位置进行查找。通过作用域链,我们可以访问到不同作用域中的变量,这样可以确保变量的可见性和封装性。
prototype (原型)
, 当我们访问⼀个对象的属性时关系: instance.constructor.prototype = instance.__proto__
特点
JavaScript 对象是通过引用来传递的, 我们创建的每个新对象实体中并没有⼀份属于自⼰的原型副本 。当我们修改原型时,与之相关的对象也会继承这⼀改变
当我们需要⼀个属性的时,Javascript 引擎会先看当前对象中是否有这个属性, 如果没有,就会查找他的 Prototype对象是否有这个属性, 如此递推下去,⼀ 直检索到 Object 内建对象.
事件代理 ( Event Delegation ), ⼜称之为 事件委托 。是 JavaScript 中常用绑定事 件的常用技巧 。顾名思义,“事件代理”即是把原本需要绑定的事件委托给父元素,让父元素担当事件监听的职务 。事件代理的原理是DOM元素的事件冒泡 。使用事件代理的好处是 可以提高性能
优点:
事件代理示例
事件代理(Event delegation)是一种常用的 JavaScript 模式,它允许我们使用单个事件处理程序来处理多个子元素的事件。
这种模式非常适合在动态添加或移除子元素的情况下,提供更高效的事件处理方式。
下面是一个事件代理的示例:
<ul id="myList">
<li>Item 1li>
<li>Item 2li>
<li>Item 3li>
<li>Item 4li>
ul>
<script>
var list = document.getElementById('myList');
function handleClick(event) {
if (event.target.tagName === 'LI') {
console.log('Clicked:', event.target.textContent);
}
}
list.addEventListener('click', handleClick);
script>
在这个示例中,我们有一个 ul
元素,并在其中包含了多个 li
子元素。我们希望在点击任何一个 li
元素时,输出被点击的文本内容。
首先,通过 document.getElementById()
方法获取到了 ul
元素的引用。然后,定义了一个名为 handleClick
的事件处理函数。在这个函数中,我们使用了事件对象 event
的 target
属性来获取触发事件的元素。
通过检查 event.target.tagName
属性,我们可以确定被点击的元素是否是 li
元素。如果是,就可以获取其文本内容,并进行相应的处理(在这里,我们简单地将文本内容输出到控制台)。
最后,通过 addEventListener
方法将 click
事件绑定到 ul
元素,并指定 handleClick
函数作为事件处理函数。这样就实现了事件代理,在点击 li
元素时触发统一的事件处理逻辑。
通过事件代理,我们可以避免给每个子元素都添加事件监听器,而是将事件处理放在父元素上进行。这样可以节省内存和提高性能,尤其在大规模或动态变化的元素列表中非常有用。
Javascript如何实现继承示例
在 JavaScript 中,可以使用以下几种方式来实现继承:
原型链继承:
原型链继承是 JavaScript 中最基本的继承方式,通过将子类的原型对象指向父类的实例来实现继承。这样,子类就可以访问到父类原型上的属性和方法。
function Parent() {
this.name = 'Parent';
}
Parent.prototype.sayHello = function() {
console.log('Hello, I am ' + this.name);
};
function Child() {
this.name = 'Child';
}
Child.prototype = new Parent();
var child = new Child();
child.sayHello(); // 输出:Hello, I am Child
构造函数继承:
构造函数继承通过在子类的构造函数中调用父类的构造函数来实现继承。这样,子类就可以继承父类的属性。
function Parent() {
this.name = 'Parent';
}
function Child() {
Parent.call(this);
this.name = 'Child';
}
var child = new Child();
console.log(child.name); // 输出:Child
组合继承:
组合继承结合了原型链继承和构造函数继承的优点,既可以继承父类的属性,又可以继承父类原型上的方法。
function Parent() {
this.name = 'Parent';
}
Parent.prototype.sayHello = function() {
console.log('Hello, I am ' + this.name);
};
function Child() {
Parent.call(this);
this.name = 'Child';
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
var child = new Child();
child.sayHello(); // 输出:Hello, I am Child
原型式继承:
原型式继承通过创建一个临时的构造函数,将父类的实例作为新创建对象的原型,从而实现继承。
function createObject(parent) {
function F() {}
F.prototype = parent;
return new F();
}
var parent = {
name: 'Parent',
sayHello: function() {
console.log('Hello, I am ' + this.name);
}
};
var child = createObject(parent);
child.name = 'Child';
child.sayHello(); // 输出:Hello, I am Child
ES6 中的 class 和 extends:
ES6 引入了 class
关键字和 extends
关键字,提供了更简洁的语法来定义类和实现继承。
class Parent {
constructor() {
this.name = 'Parent';
}
sayHello() {
console.log('Hello, I am ' + this.name);
}
}
class Child extends Parent {
constructor() {
super();
this.name = 'Child';
}
}
var child = new Child();
child.sayHello(); // 输出:Hello, I am Child
以上是几种常见的继承方式,每种方式都有各自的优缺点。
在选择实现继承方式时,可以根据具体需求和场景来选择适合的方式。
在 JavaScript 中,this
是一个特殊的对象,它在函数被调用时自动创建,并且指向调用该函数的上下文对象。
this
的值取决于函数是如何被调用的,它可以有多种不同的值。
具体来说,以下是 this
的一些常见用法和理解:
this
,它指向全局对象(在浏览器环境中通常是 window
对象)。this
取决于函数的调用方式。如果函数是作为对象的方法被调用(例如 obj.method()
),则 this
指向调用该方法的对象(obj
)。如果函数是以函数形式被调用(例如 func()
),则 this
取决于函数的调用上下文,通常是全局对象(浏览器环境下是 window
)。this
指向新创建的实例对象。call()
或 apply()
方法调用函数时,可以显式地指定 this
的值。this
与所在的词法作用域保持一致,而不是动态绑定。总之,this
提供了一种在函数内部引用当前执行上下文的机制,它的值在函数被调用时动态确定。要理解 this
的具体值,需要考虑函数的调用方式和上下文环境。
This对象示例
当谈到 this
对象的示例时,以下是一些常见的用法和场景:
this
对象示例:console.log(this); // 在浏览器环境中通常指向 window 对象
this
对象示例:const obj = {
name: 'Alice',
sayHello: function() {
console.log('Hello, ' + this.name);
}
};
obj.sayHello(); // 输出:Hello, Alice
this
对象示例:function Person(name) {
this.name = name;
}
const person = new Person('Bob');
console.log(person.name); // 输出:Bob
call()
或 apply()
改变 this
对象示例:const obj1 = { name: 'Alice' };
const obj2 = { name: 'Bob' };
function greet() {
console.log('Hello, ' + this.name);
}
greet.call(obj1); // 输出:Hello, Alice
greet.call(obj2); // 输出:Hello, Bob
this
对象示例:const obj = {
name: 'Alice',
sayHello: function() {
setTimeout(() => {
console.log('Hello, ' + this.name);
}, 1000);
}
};
obj.sayHello(); // 输出:Hello, Alice(箭头函数保持词法作用域的 this 值)
这些示例展示了 this
对象在不同情况下的使用方式和取值。注意,在实际编程中,this
的行为可能会更加复杂,因此在使用时需要谨慎理解和处理。
W3C 中定义事件的发生经历三个阶段:捕获阶段 ( capturing ) 、 目标阶段 ( targetin ) 、冒泡阶段 ( bubbling )
在JavaScript中,new
操作符用于创建一个对象实例。使用new
操作符时,它会执行以下步骤:
__proto__
)设置为构造函数的原型(即构造函数的 prototype
属性)。this
指向这个新对象。下面是一个示例来说明new
操作符的作用:
// 定义一个构造函数
function Person(name, age) {
this.name = name;
this.age = age;
this.sayHello = function() {
console.log("Hello, my name is " + this.name);
};
}
// 使用new操作符创建一个Person对象实例
var person1 = new Person("Alice", 25);
console.log(person1.name); // 输出: "Alice"
console.log(person1.age); // 输出: 25
person1.sayHello(); // 输出: "Hello, my name is Alice"
在上面的示例中,我们定义了一个构造函数Person
,它有两个参数name
和age
,以及一个方法sayHello
。当我们使用new
操作符创建一个对象实例person1
时,new
操作符会执行以下操作:
person1
。person1
对象的原型(__proto__
)设置为Person.prototype
。Person
的执行上下文中,将构造函数内部的this
指向person1
对象。person1
的属性。在这个例子中,person1.name
被设置为"Alice"
,person1.age
被设置为25
。Person
构造函数中定义的sayHello
方法也被添加到了person1
对象上。new
操作符返回这个新创建的对象实例person1
。通过使用new
操作符,我们可以创建一个带有特定属性和方法的对象实例,并且该对象实例可以通过构造函数中的this
关键字进行访问和操作。
Ajax 的原理简单来说是 在用户和服务器之间加了—个中间层( AJAX 引擎), 通过 XmlHttpRequest 对象来向服务器发异步请求,从服务器获得数据,然后用 javascript 来操作 DOM 而更新页面 。使用户操作与服务器响应异步化 。这其中最关键的⼀步就是从服务器获得请求数据
Ajax 的过程只涉及 JavaScript 、 XMLHttpRequest 和 DOM 。
XMLHttpRequest 是 aja x的核⼼机制
ajax 有那些优缺点?
优点:
缺点:
首先了解下浏览器的 同源策略
同源策略 /SOP ( Same origin policy) 是⼀ 种约定, 由Netscape公司1995年引⼊浏览器, 它是浏览器最核⼼也最基本的 安全功能, 如果缺少了同源策略, 浏览器很容易受到 XSS 、 CSFR 等攻击 。 所谓同源是指"协议+域名+端口"三者相同, 即便两个不同的域名指向同⼀个ip 地址,也非同源
那么怎样解决跨域问题的呢?
在 JavaScript 中,解决跨域问题的常见方法包括以下几种:
// 客户端代码
fetch('http://api.example.com/data', {
method: 'GET',
headers: {
'Origin': 'http://yourdomain.com'
}
})
.then(response => response.json())
.then(data => {
// 处理返回的数据
})
.catch(error => {
// 处理错误
});
在服务器端的响应头中设置相关的 CORS 头信息:
Access-Control-Allow-Origin: http://yourdomain.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type
标签可以跨域加载资源的技术。通过动态创建一个
标签,并将跨域请求封装为一个回调函数,服务器返回的数据会作为参数传递给回调函数。例如:function handleResponse(data) {
// 处理返回的数据
}
var script = document.createElement('script');
script.src = 'http://api.example.com/data?callback=handleResponse';
document.body.appendChild(script);
const express = require('express');
const fetch = require('node-fetch');
const app = express();
app.get('/api/data', (req, res) => {
const url = 'http://api.example.com/data';
fetch(url)
.then(response => response.json())
.then(data => res.json(data))
.catch(error => res.status(500).json({ error: 'Something went wrong' }));
});
app.listen(3000, () => {
console.log('代理服务器已启动,监听端口 3000');
});
通过访问代理服务器的 /api/data
路径来获取跨域资源。
这些方法可以用来解决跨域问题,具体选择哪种方法取决于你的需求和实际情况。