前端(一):基础知识

207 阅读26分钟

事件

传播机制 - 事件流

当在 DOM 中触发一个事件(如点击、鼠标移动等)时,这个事件不仅仅是在一个元素上发生,它实际上会经历一个或多个元素。这些元素构成了一个事件传播的路径,称为"事件流"。

DOM 中的事件流包括三个阶段:捕获阶段(Capturing Phase)、目标阶段(Target Phase)和冒泡阶段。

  • 捕获阶段:事件从 document 对象开始,沿着 DOM 树向下传播,事件会经过路径上的每一个元素,直到它到达触发事件的目标元素。在这个阶段,但只有那些为捕获阶段注册了事件监听器的元素才会执行其事件处理函数。

  • 目标阶段:当事件到达目标元素时,会触发目标元素上注册的事件监听器(无论是为捕获阶段还是冒泡阶段注册的)。这个阶段是事件流中的一个重要部分,但通常与捕获阶段或冒泡阶段一起讨论。

  • 冒泡阶段:事件从目标元素开始,沿着 DOM 树向上传播,事件会经过路径上的每一个元素,直到它到达 document 对象。在这个阶段,只有那些为冒泡阶段注册了事件监听器的元素才会执行其事件处理函数。

在一个元素及其祖先元素都绑定了相同类型的事件监听器时,捕获阶段的事件监听器会先于冒泡阶段的事件监听器被触发。

例如,在 div 和 body 都绑定了 click 事件的情况下,如果点击了 div,在捕获阶段,body 的 click 事件监听器会先被触发,随后才是 div 的 click 事件监听器(如果设置了捕获)。而在冒泡阶段,div 的 click 事件监听器会先被触发,随后冒泡到 body,触发 bod y的 click事件监听器(如果未设置捕获)。

当使用 addEventListener 方法为元素注册事件监听器时,如果不明确指定第三个参数,那么默认情况下,该监听器会在冒泡阶段被触发(即未设置捕获)。

注意需要是相同类型的事件,一个事件的发生不会直接导致另一个不同类型的事件处理器的执行。

事件委托

  • 捕获阶段:虽然捕获阶段存在,但在实际开发中,其应用相对较少。

  • 冒泡阶段:冒泡阶段的应用更为广泛。例如,开发人员可以利用冒泡阶段来监听多个元素的共同事件,从而减少代码的重复性。此外,事件委托也是冒泡阶段的一个重要应用,通过将事件监听绑定在目标元素的祖先元素上,可以实现对动态添加的子元素的事件监听,提高页面性能。

由于事件冒泡的存在,你可以将事件监听器添加到父元素上,以捕获子元素上的事件。这被称为事件委托,它可以用于减少内存消耗并提高性能。

事件对象 - event

当事件被触发时,浏览器会创建一个事件对象,并将其作为参数传递给事件处理函数。这个事件对象包含了与事件相关的所有信息,如事件的类型、触发事件的目标元素、鼠标的位置等。通过事件对象,我们可以获取这些信息,并在事件处理函数中使用它们。

event 常用属性和方法:

type返回事件的名称,如 click、keydown 等。
target返回触发事件的目标元素。
currentTarget对于使用事件委托的情况,currentTarget 表示事件绑定的那个元素,而 target 则表示事件实际发生的元素。
timeStamp返回事件创建的日期和时间(以毫秒为单位)。
preventDefault()调用此方法可以阻止事件的默认行为。例如,阻止表单的提交或阻止链接的导航。
stopPropagation()调用此方法可以阻止事件进一步冒泡到 DOM 树中的上层元素。

事件监听器 - addEventListener

事件监听器(或称为事件处理程序)是一个函数,当指定的事件在指定的元素上发生时,该函数会被调用。我们可以使用 addEventListener() 方法来为元素添加事件监听器。

鼠标事件

click当用户点击某个对象时触发
dblclick当用户双击某个对象时触发
mousedown当鼠标按钮被按下时触发
mouseup当鼠标按键被松开时触发
mouseenter当鼠标指针移动到元素上时触发
mouseleave当鼠标指针移出元素时触发
mouseover当鼠标移到某元素之上时触发
mouseout当鼠标从某元素移开时触发
mousemove当鼠标在元素内部移动时触发
contextmenu当用户点击鼠标右键打开上下文菜单时触发

键盘事件

keydown当某个键盘按键被按下时触发。
keypress当某个键盘按键被按下并松开时触发(注意:在某些浏览器中,keypress 事件可能不会触发对于某些特殊键,如修饰键)。
keyup当某个键盘按键被松开时触发。

举例:

// 获取输入框元素
const inputElement = document.getElementById('myInput');
 
// 定义当按下回车时执行的函数
function handleEnterPress(event) {
  if (event.keyCode === 13) {
    // 在这里编写你要执行的代码
    console.log('回车键被按下了!');
  }
}
 
// 为输入框添加键盘事件监听器
inputElement.addEventListener('keydown', handleEnterPress);

表单事件

submit当表单提交时触发
change当表单元素的内容改变时触发(如 <input>, <textarea>, <select> 等)
blur当元素失去焦点时触发
focus当元素获取焦点时触发
focusin当元素即将获取焦点时触发
focusout当元素即将失去焦点时触发
input当元素获取用户输入时触发(如实时输入时)
reset当表单重置时触发

举例:

<!DOCTYPE html>  
<html lang="en">  
    <head>  
        <meta charset="UTF-8">  
        <meta name="viewport" content="width=device-width, initial-scale=1.0">  
        <title>表单事件示例</title>  
    </head>
    
    <body>  
        <form id="myForm">  
            <label for="username">用户名:</label>  
            <input type="text" id="username" name="username" required>  
            <br>  
            <label for="password">密码:</label>  
            <input type="password" id="password" name="password" required>  
            <br>  
            <input type="submit" value="提交">  
        </form>  

        <script src="script.js"></script>  
    </body>  
</html>
document.addEventListener('DOMContentLoaded', function() {  
    // 获取表单元素  
    var form = document.getElementById('myForm');  
  
    // 为表单添加 submit 事件监听器  
    form.addEventListener('submit', function(event) {  
        // 阻止表单的默认提交行为,以便我们可以执行自定义操作  
        event.preventDefault();  
  
        // 验证表单输入(这里只是简单示例)  
        var username = document.getElementById('username').value;  
        var password = document.getElementById('password').value;  
  
        if (username === '' || password === '') {  
            alert('用户名和密码不能为空!');  
            return;  
        }  
  
        // 假设验证通过,执行其他操作(例如,发送AJAX请求)  
        console.log('表单数据:', { username: username, password: password });  
  
        // 如果需要提交表单到服务器,可以取消注释下面的代码  
        // form.submit(); // 但在实际应用中,通常会使用AJAX来异步发送数据  
    });  
});

窗口和文档事件

DOMContentLoaded当 HTML 文档被完全加载和解析完成之后触发
load在整个页面及所有依赖资源如样式表和图片都已完成加载后触发
resize当窗口大小改变时触发
scroll当文档被滚动时触发
beforeunload当即将离开页面(刷新或关闭)时触发
unload当用户退出页面时触发(注意:由于安全和性能原因,此事件在大多数现代浏览器中可能无法正常工作)

DOMContentLoaded 是一个事件,当 HTML 文档被完全加载和解析完成之后,就会触发这个事件,而无需等待样式表、图像和子框架完成加载。换句话说,当文档结构(DOM)加载解析完成后,DOMContentLoaded 事件就会被触发,这时你可以安全地运行依赖于 DOM 的 JavaScript 代码。

在大多数现代浏览器中,DOMContentLoaded 事件要早于 load 事件触发。load 事件会在整个页面及所有依赖资源如样式表和图片都已完成加载后触发。

在 JavaScript 中,你通常会使用 addEventListener 方法来监听 DOMContentLoaded 事件,以确保你的代码在 DOM 完全可用之后执行。

其他事件

hashchange当当前 URL 的锚部分(hash)发生修改时触发
abort当图像的加载被中断时触发
error在加载文档或图像时发生错误时触发

JavaScript 常用事件

  • onload 网页加载事件
  • onclick 点击事件
  • ondblclick 双点事件
  • onmouseover 光标移上标签触发
  • onmouseout 光标移走时触发
  • onmousedown 光标按下事件
  • onmouseup 光标弹起事件

click 和 onclick 的区别:

  • click 是一个 JavaScript 中的事件,是写在 JavaScript 代码中的

  • onclick 是一个 HTML 事件属性,是写在 HTML 元素标签中的

<script>
document.addEventListener('DOMContentLoaded', function() {
   // 初始化代码放置在这里
});

document.addEventListener('load', function() {
   // load 事件
   // 可以设置多个监听器,可以写多个
});

window.onload = function () {
   // 功能同上
   // 只能设置一个监听器,写多个会被覆盖
}
</script>

作用域

作用域即变量/函数可以被访问的区域。

作用域可以分为:全局作用域、块级作用域、函数作用域、模块作用域

全局作用域

{ } 和函数外的区域就是全局作用域。

  • 全局作用域在页面打开时创建,在页面关闭时销毁

  • 全局作用域中有一个全局对象 window,它代表的是一个浏览器的窗口,由浏览器创建,可以直接使用,全局变量是 window 对象的属性,函数是 window 对象的方法

  • 全局作用域中的声明的变量是全局变量,在页面的任意的部分都可以访问

  • 全局作用域中无法访问函数作用域的变量

  • 没有使用修饰符(var、let、const)声明的变量是全局变量

块级作用域

{ } 所包裹起来的区域就是块级作用域,也称代码块。

ES5 本身不提供块级作用域,通常使用立即调用函数表达式实现:

(function () {
  var block_scoped=0;
}());
console.log(block_scoped); // 引用错误

ES6 中,使用 let 和 const 实现块级作用域。

这句话的意思是:使用 let 和 const 声明的变量或常量的作用域被限制在块的内部,意味着这些变量或常量在块外部是不可见的,也无法被访问。这与 ES5 的 var 不同,var 声明的变量具有全局作用域(即在全局作用域内可用)。例如:

console.log(a) // undefined 变量提升
{
    var a = 1;
    let b = 2;
    const c = 3;
}
console.log(a) // 1
console.log(b) // ReferenceError: b is not defined
console.log(c) // ReferenceError: c is not defined

块级作用域优点:

  • 避免变量提升

  • 减少命名冲突

  • 增强代码可读性

函数作用域

函数内的区域为函数作用域(即 function 中 { } 内的部分)。

函数作用域中有一个概念,叫"执行上下文"。什么是执行上下文呢?

当函数执行时,会创建一个执行期上下文的内部对象。每调用一次函数,就会创建一个新的上下文对象,他们之间是相互独立的。当函数执行完毕,它所产生的执行期上下文会被销毁。

执行上下文的作用:

  • 变量和参数管理:每当函数被调用时,都会创建一个新的执行上下文,其中包含该函数的参数和局部变量。执行上下文可以确保变量和参数只在函数内部可见和可访问,从而避免了变量名的冲突和污染全局作用域。

  • 作用域链的创建:执行上下文还包含了作用域链(Scope Chain),这是一个从当前执行上下文的变量对象开始,逐级向上链接到全局执行上下文的变量对象的链表。作用域链的存在使得函数能够访问到其外部(父级)作用域中的变量,这是实现闭包等高级功能的基础。

  • this 值的确定:在 JavaScript 中,this 是一个特殊的关键字,它指向函数执行时的上下文对象。在执行上下文中,this 的值会根据函数的调用方式来确定,例如,在全局环境中调用函数时,this 指向全局对象(在浏览器中是 window),在对象方法中调用时,this 指向该对象。

    • 这里要注意 this 并不等价于执行上下文。执行上下文是一个更广泛的概念,它包含了 this 值、变量、作用域链等多种状态信息。而 this 只是执行上下文中的一个特定属性。

函数作用域的特点:

  • 函数作用域中可以访问到全局作用域的变量

  • 全局作用域无法访问函数内定义的变量

  • 函数的参数就相当于在函数内部使用 let 声明的变量

  • 函数声明可以提升

    // 函数提升
    test2() // 1
    function test2() { ... } // 正常执行不会报错
    

模块作用域

ES6 新增语法,每个 JavaScript 文件都是一个模块,每个模块中的区域即模块作用域,其他文件要使用模块中的变量/函数,必须对外导出模块中的变量/函数,并在目标文件中引入。

选择排序和冒泡排序

它们主要用于实现对无序的数字集合进行排序

选择排序

是从起始位置开始,找最小的数值所在的索引,如果最终存储的索引不是起始位置,就与起始位置交换存储数据,每执行一次循环,会将最小值存储在起始位置上。

在这里插入图片描述

var arr=[2,5,6,7,4,9,13,8];
for(var j=0;j<=arr.length-2;j++){
    var min=j;
    for(var i=j+1;i<=arr.length-1;i++){
        if(arr[min]>arr[i]){
            min=i;
        }
    }
    if(min!=j){
        var middle=0;
        middle=arr[j];
        arr[j]=arr[min];
        arr[min]=middle;
    }
}
console.log(arr)

冒泡排序

从左到右依次比较两个存储数据的大小,如果第一个数大于第二个数,就交换两个数据,这样一轮比较之后,最大的数会放在后面,这样,每次循环比较,本轮中的最大值都会排到最后,直到循环结束,实现数组升序。

在这里插入图片描述

var arr=[2,5,6,7,4,9]
    for(j=0;j<=(arr.length-1)-1;j++){
        for(var i=0;i<=(arr.length-1)-1-j;i++){
            if(arr[i]>arr[i+1]){
                var middle=0;
                middle=arr[i];
                arr[i]=arr[i+1];
                arr[i+1]=middle;
            }
        }
    }
}
console.log(arr)

选择排序发生大小问题时,只是做赋值索引的操作,循环结束,才发生一次交换数据的操作。

冒泡排序,每发生一次大小顺序问题,就交换一次数据。

冒泡排序执行数据交换的操作比较繁琐,执行效率低于选择排序。

JavaScript 垃圾回收机制

垃圾回收是一种自动内存管理机制,用于检测和清除不再使用的对象,以释放内存空间。当一个对象不再被引用时,垃圾回收器会将其标记为垃圾,然后在适当的时候清除这些垃圾对象,并将内存回收给系统以供其他对象使用。

"适当的时候"是指什么时候?

首先 JavaScript 引擎会定期找出垃圾,也会在内存使用量到达某一阈值或内存分配失败时去找垃圾,还有一些其他的机制这里不做拓展。

什么是"对象不再被引用"?

当一个对象不再被任何变量或属性引用时,它就成为垃圾。例如,当一个函数执行完毕后,其中创建的局部变量将成为垃圾,因为它们无法再被访问到。

JavaScript 引擎是如何找到并清理垃圾的?

有两种方式:标记清除法和引用计数法。现在大都用的是标记清除法,因为引用计数法在处理循环引用时存在问题。

对于栈中的垃圾和堆中的垃圾,清理时有什么区别?

当我们在 JavaScript 中讨论垃圾回收机制时,主要关注的是堆内存中的回收机制。栈内存的管理是由编译器和解释器自动进行的,当执行上下文被销毁时,其所占用的栈内存会自动被回收。

栈中的垃圾清理堆中的垃圾清理
存储内容基本类型数据、函数调用上下文复杂数据结构(对象和数组)
管理方式自动管理,由编译器和解释器负责依赖垃圾回收机制,自动回收不再被引用的对象
垃圾清理方式无需显式清理,执行上下文销毁时自动释放内存通过垃圾回收机制周期性检查并回收不再被引用的对象内存

标记清除

它分为两个阶段:标记阶段和清除阶段。

  1. 标记阶段: 在标记阶段,垃圾回收器会对内存中的所有对象进行遍历,从根对象开始(通常是全局对象)递归地遍历对象的引用关系。对于每个被访问到的对象,垃圾回收器会给它打上标记,表示该对象是可达的,即不是垃圾。这个过程确保了所有可达对象都会被标记。

  2. 清除阶段: 在清除阶段,垃圾回收器会遍历整个内存,对于没有标记的对象,即被判定为垃圾的对象,会被立即回收,释放内存空间。这样,只有被标记的对象会被保留在内存中,而垃圾对象会被清除。

8a60dafb39eda288d9427e263979c89.jpg

上图中蓝色的元素代表被访问到的对象,即可达对象,灰色代表没有被访问到的对象,即不可达对象

如何理解"递归地遍历对象的引用关系"?

在 JavaScript 中,内存中的对象通过引用关系相互连接,形成了复杂的引用图。

当 JavaScript 代码执行时,会创建各种对象,包括全局对象、函数对象、数组对象、普通对象等等。这些对象在内存中占据一定的空间。

随着程序的执行,对象之间会建立引用关系。这种引用关系通常是通过将一个对象的引用赋值给另一个对象的属性、局部变量或函数的参数来实现的。

例如你在全局作用域中写了这行代码 var obj = {},这就代表着你给全局对象设置了一个 obj 属性;在例如一个函数将一个对象作为参数,实际上传递的是对象的引用...这些都会成为引用关系的一部分。通过这些引用关系,JavaScript 引擎会动态地构建一个复杂的引用图。这个引用图以根对象为起点,通过对象之间的引用关系不断扩展和延伸。

标记阶段完成之后,如果其中一个变量不再被引用了,此时该变量还是被标记状态的,那么清除阶段怎么识别它呢?

在本次清除阶段识别不了,但是在下一轮的标记阶段,该变量不会被打上标记,那它就可以被清除啦。

如果一个变量在标记阶段没有被引用,它没有被打上标记,但是在清除阶段之前,它又被引用了,那它会被视作垃圾回收吗?

无需担心,现代垃圾回收器通常会使用各种策略(如写屏障、增量标记、并发标记等)来跟踪引用变化,不会错误地回收仍然被引用的对象。

如何理解根对象?

根对象通常包括以下几类:

  1. 全局对象:在浏览器中,全局对象是 window(Node.js 中是 global),它包含了全局作用域中定义的所有属性和方法。全局对象总是可达的,因为它是程序的入口点之一,这也意味着全局对象永远不会被垃圾回收机制视为垃圾对象来回收。

  2. 活跃的执行上下文(Execution Contexts) :可以理解为当前正在执行或即将执行的代码块所对应的执行上下文。"活跃的执行上下文"可以是全局执行上下文,也可以是某个函数执行上下文,具体取决于当前执行的代码块。全局对象其实就是全局执行上下文中的一个组成部分。

  3. 内置的持久性引用:有些 JavaScript 引擎可能会维护一些内置的、持久的引用,这些引用指向的对象也会被视为根对象。例如,一些引擎可能会将当前执行的栈帧或特定类型的内部数据结构保留在内存中,以便进行调试或性能分析。

引用计数

它的策略是每个对象维护一个计数器来记录其被引用的次数,当对象被新引用时计数加 1,当引用被移除时计数减 1,当计数减至 0 时对象被自动回收。

两者优缺点比较

标记清除:

优点:

  • 简单有效

  • 可以处理循环引用

缺点:

  • 标记清除算法会暂停程序的执行,也就是说垃圾回收过程中的停顿

  • 标记清除算法会在回收过程中产生大量的不连续的、碎片化的内存空间,从而使得内存的利用率降低。

引用计数:

优点:

  • 简单高效

  • 实时回收:用计数可以在对象不再被引用时立即回收,不需要等待垃圾收集器的运行。

缺点:

  • 循环引用:当两个或多个对象相互引用时,它们的引用计数都不为零,即它们无法被回收,这会导致内存泄漏

JavaScript 哪些操作会导致内存泄漏?

全局变量导致内存泄漏

如果意外地将变量声明为全局变量(例如,在函数外部或在函数内部但未使用 varletconst 声明的变量),那么这些变量在程序的整个生命周期内都将保持活动状态,可能导致内存泄漏。

function myFunction() {  
    a = "I am a global variable now!"; // 忘记使用 var, let 或 const
}  
  
myFunction();  
// 此时 a 成为了全局变量,即使函数执行完毕,a 也不会被回收

闭包

function createLeakyClosure() {  
    var localVar = "我是局部变量,但由于闭包,我可能导致内存泄漏";  
  
    // 返回一个匿名函数,该函数引用了localVar  
    return function() {  
        return localVar;  
    };  
}  
  
// 外部引用闭包  
var closure = createLeakyClosure();  
  
// 如果 closure 不再需要,但由于它是闭包,它仍然持有对 localVar 的引用,  
// 这可能导致 localVar 占用的内存无法被回收,直到 closure 被明确置为 null 或 undefined。

DOM 引用未释放

如果一个 DOM 元素被从 DOM 树中移除,但仍有 JavaScript 变量或对象保持对该 DOM 元素的引用,那么这块 DOM 元素占用的内存(包括其属性、事件监听器等)将不会被垃圾回收机制回收,从而造成了内存泄漏。

例如有一个长时间存活的对象,它有一个属性的值是该 DOM 元素,即使你通过操作 DOM 树删除了该 DOM 元素,该 DOM 元素所占据的内存空间不会被视作垃圾回收,直到这个长时间存活的对象被销毁。

定时器/计时器

没有清除定时器,导致计时器持续运行,占用内存空间,导致内存泄漏。尤其是在 vue 中,最好是在 beforeDestory() 中清除定时器

var let const 区别

  1. var 命令会发生变量提升现象,即变量可以在声明之前使用,值为 undefined。let 和 const 不会变量提升。
/*
 * 变量提升意味着变量声明会被提升到其作用域的顶部,
 * 但初始化(即赋值)操作不会,
 * 因此,在变量被实际赋值之前,它的值是 undefined。
 */
console.log(a); // 输出 undefined
var a = 2;

// let 的情况
console.log(b); // 报错 ReferenceError,暂时性死区
let b = 2;

这里有个概念叫"暂时性死区",是 let 和 const 的概念,暂时性死区指的是在代码块内,使用 let 或 const 命令声明变量之前,该变量都是不可用的。在这段时间内,如果尝试访问该变量,将会抛出ReferenceError

var 没有暂时性死区的概念,因为 var 会变量提升。

  1. let 和 const 只在块级作用域内有效,var 可以跨块访问(不能跨函数访问)
if(true){
    var a = 0
    let b = 0
    const c = 0
}
console.log(a); // 0
console.log(b); // 报错 ReferenceError b is not defined
console.log(c); // 报错

// 倒过来
console.log(a); // undefined
console.log(b); // 报错 ReferenceError b is not defined
console.log(c); // 报错
if(true){
    var a = 0
    let b = 0
    const c = 0
}
console.log(window.a) // undefined

// 不能跨函数访问
function test() {
    var d = 0
}
console.log(d) // undefined
  1. var 声明的变量为全局变量,let 和 const 不是
var a = 0
let b = 0
const c = 0
console.log(window.a); // 0
console.log(window.b); // undefined
console.log(window.c); // undefined
  1. var 可以重复声明,let 和 const 不行
var a = 0
var a = 1
console.log(a); // 1
let b = 0
let b = 1
console.log(b); // Identifier 'b' has already been declared
const c = 0
const c = 1
console.log(c); // Identifier 'c' has already been declared
  1. var 和 let 可以不用设置初始值,const 声明变量必须设置初始值
var a;
let b;
const c; // 语法错误
  1. let 和 var 创建的变量是可以更改指针指向(可以重新赋值)。const 声明的变量是不允许改变指针的指向。

Canvas

Canvas 是 HTML5 提供的一种绘图 API,它可以通过 JavaScript 在网页上动态绘制图形和动画。

<canvas> 标签只是图形容器,必须使用脚本来绘制图形。

Canvas 坐标系统:

Canvas 的坐标系统是一个二维坐标系,用于确定 Canvas 上元素的位置。

Canvas 坐标系统以左上角为原点,水平方向向右增加,垂直方向向下增加。

x 轴表示水平位置,从左到右递增,y 轴表示垂直位置,从上到下递增。

在 Canvas 中,可以使用坐标系统来确定绘图元素的位置,例如点、线、图形等。

使用示例:

<html>
<body>
  <!--1. 在 HTML 页面中创建一个 Canvas 元素-->
  <canvas id="myCanvas" width="200" height="200"></canvas>
  <!-- width 和 height 的单位都是 px -->
  
  <script>
    // 2. 使用 JavaScript 获取 Canvas 元素的引用并获取绘图上下文
    const canvas = document.getElementById('myCanvas');
    const ctx = canvas.getContext('2d'); // 2d 是当前唯一合法值
    
    /* 也可以在脚本中设置尺寸
    canvas.width = 500;
    canvas.height = 300;
    */
    
    // 3. 填充颜色
    ctx.fillStyle = 'red';
    
    // 4. 绘制矩形
    ctx.fillRect(50, 50, 100, 100);
  </script>
</body>
</html>

常用 API:

  • ctx.moveTo(x, y):将绘图游标移动到指定的坐标点(x, y)。

  • ctx.lineTo(x, y):从当前绘图位置绘制一条直线到指定的坐标点(x, y)。

  • ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise):绘制一段圆弧路径,以坐标(x, y)为圆心,radius 为半径,从 startAngle 开始到 endAngle 结束,anticlockwise 表示是否逆时针绘制。

  • ctx.fillRect(x, y, width, height):绘制一个填充色的矩形,以坐标(x, y)为左上角,width 为宽度,height 为高度。

  • ctx.beginPath():开始一条新的路径

  • ctx.lineWidth = 5:设置线段宽度

  • ctx.strokeStyle= "blue":设置描边颜色

  • ctx.fillStyle="blue":设置填充颜色

  • ctx.stoke():描边动作

  • ctx.fill():填充动作

  • ctx.closePath():闭合路径

常用示例:

  1. 画一条直线:

    ctx.beginPath();  
    ctx.moveTo(50, 50); // 起点坐标  
    ctx.lineTo(150, 150); // 终点坐标  
    ctx.stroke(); // 绘制直线
    
  2. 画圆:

    const centerX = canvas.width / 2; // 圆心的x坐标
    const centerY = canvas.height / 2; // 圆心的y坐标
    const radius = 50; // 圆的半径
    const startAngle = 0; // 圆的起始角度
    const endAngle = 2 * Math.PI; // 圆的结束角度
    
    ctx.beginPath(); // 开始新的路径
    ctx.arc(centerX, centerY, radius, startAngle, endAngle); // 绘制圆形路径
    ctx.stroke(); // 绘制路径
    
  3. 绘制文本

    ctx.font = '24px Arial'; // 设置字体样式和大小
    ctx.fillStyle = 'red'; // 设置字体颜色
    
    const text = 'Hello, 阿珊和她的猫!'; // 要绘制的文本
    const x = 50; // 文本的x坐标
    const y = 100; // 文本的y坐标
    
    ctx.fillText(text, x, y); // 绘制填充文本
    // ctx.strokeText(text, x, y); // 绘制描边文本(可选)
    

定时器

  • setInterval:以设定的时间间隔重复执行函数

  • setTimeout:在设定延迟时间之后执行一次函数

do 表达式

本质上,块级作用域是一个语句,将多个操作封装在一起,没有返回值。

{
  let t = f();
  t = t * t + 1;
}

上面代码中,块级作用域将两个语句封装在一起。但是,在块级作用域以外,没有办法得到 t 的值,因为块级作用域不返回值,除非 t 是全局变量。

现在有一个提案,使得块级作用域可以变为表达式,也就是说可以返回值,办法就是在块级作用域之前加上 do,使它变为 do 表达式。

let x = do {
  let t = f();
  t * t + 1;
};

上面代码中,变量 x 会得到整个块级作用域的返回值。

循环

  • forEach 和 map 只能用 retur 跳出本次循环,不能用 continue 跳出也不能用 break 终止循环,可以用抛出异常的方式跳出循环。

  • for 循环可以使用 break 中断整个循环(当有多层嵌套时,只中断自己),使用 comtinue 跳过本次循环进入下一次循环,使用 rertrn 会报错

  • 但是可以在 for 循环外层写一个有返回值的函数,这时候在 for 循环中使用 return 可以跳出整个循环

锚点

用于快速跳转到页面的某个位置。

锚点访问方式:

  1. 利用超链接

    <a href="#top">点击我链接到TOP</a> 
    <a href="#content">点击我链接到CONTENT</a>  
    <a href="#foot">点击我链接到FOOT</a>
    
  2. 页面地址后面加锚点标记

    <!-- 假如本页面的地址是 -->  
    http://文件路径/index.html  
    
    <!-- 要访问 foot 锚点只要访问如下链接即可 -->  
    http://文件路径/index.html#foot
    

埋点

埋点(也称为数据埋点或代码埋点)是一种技术方法,用于在应用或网站的页面中嵌入特定的代码或脚本,以收集和分析用户的行为数据。

埋点的目的是为了获取并统计网站或应用的访问信息,包括但不限于页面点击量、用户访问量、用户访问路径、用户转化率、用户访问时长等关键指标。

标签

link

link 标签用于建立与外部资源的关联,如样式表、图标、JS 等。只能位于 head 部分,可以出现多次。

<head>
    <link rel="stylesheet" type="text/css" href="style.css" />
</head>
属性描述
rel规定当前文档与被链接文档之间的关系。
href规定被链接文档的位置。
type规定被链接文档的 MIME 类型。