CSS盒模型本质上是一个盒子,封装周围的HTML元素,它包括:外边距(margin)、边框(border)、内边距(padding)、实际内容(content)四个属性,分为标准模型和IE模型,标准盒模型的宽度不包括内边距,IE模型的宽度要包括内边距。
content:即实际内容,显示文本和图像;
border:即边框,围绕元素内容的内边距的一条或多条线,由粗细、样式、颜色三部分组成;
padding:即内边距,清除内容周围的区域,内边距是透明的,取值不能为负,受盒子的background属性影响;
margin:即外边距,在元素外创建格外的空白,空白通常指不能放其他元素的区域。
这些部分相互之间有一定的关系,例如,盒子的总宽度可以通过内容宽度、内边距宽度、边框宽度和外边距宽度的和来计算。
盒子模型在网页布局中非常重要,通过设置不同的尺寸和样式,可以实现元素的定位、大小调整和间距控制等。CSS中提供了一系列的属性,用于控制和调整盒子模型的各个部分,例如width、height、padding、border、margin等。
盒模型分为W3C标准盒模型和怪异盒模型
标准盒模型和IE盒模型的区别在于设置width和height时,所对应的范围不同:
标准盒模型中width和height属性的范围指的就是content部分的
IE盒模型中width和height属性的范围指的就是content+border+padding在三个加起来的
切换盒模型的时候,可以借助CSS中的属性box-sizing属性
box-sizing:content-box是W3C盒子模型
box-sizing:border-box是IE盒模型
怪异盒模型的使用场景:
兼容性问题:一些旧版本的浏览器可能不正确地解析标准盒模型,导致页面的布局出现问题。在这种情况下,可以通过设置怪异盒模型来解决兼容性问题
维护旧代码:如果正在维护一个旧的网站,该网站使用怪异盒模型,而你并不想或无法修改现有代码,那么你可能需要继续使用怪异盒模型以保持页面的一致性和正确性。
id选择器>类选择器>属性选择器>伪类选择器>标签选择器>伪元素选择器>相邻兄弟选择器>子选择器>后代选择器>通配符选择器
对于选择器的优先级:
标签选择器、伪元素选择器:1
类选择器、伪类选择器、属性选择器:10
Id选择器:100
内联样式:1000;
PS:
!Important声明的样式优先级最高;
如果优先级相同,则最后出现的样式生效;
继承得到的样式的优先级最低;
通用选择器、子选择器和相邻兄弟选择器并不在这四个等级中,所以他们的权值都为0;
样式表的来源不同时,优先级顺序为:内联样式>内部样式>外部样式>浏览器用户自定义样式>浏览器默认样式
继承的属性
字体系列属性
font-family:字体系列
font-weight:字体的粗细
font-size:字体的大小
font-style:字体的风格
文本系列属性
text-index:文本缩进
text-align:文本水平对齐
line-height:行高
word-spacing:单词之间的间距
letter-spacing:中位或者字母之间的间距
text-transform:控制文本大小写(就是uppercase、lowercase、capitalize)
color:文本颜色
元素可见性
visibility:控制元素显示隐藏
列表布局属性
list-style:列表风格,包括list-style-type、list-style-image等
光标属性
cursor:滚滚表显示为何种形态
定位属性
伪类选择器和伪元素选择器的区别?
两者都是CSS中用于选择文档中特定状态或位置的元素的特殊选择器。它们之间有一些重要的区别:
伪类选择器
伪类选择器用于选择处于特定状态的元素,如鼠标悬停状态、被点击状态、第一个子元素等
伪类选择器以冒号(:)开头,后面跟着伪类的名称,如:hover,:active,:first-child等。
伪类选择器可以与其他选择器(如元素选择器、类选择器、ID选择器等)组合使用,以缩小选择范围。例如,a:hover选择所有被鼠标悬停的链接元素
伪元素选择器
用于选择元素的特定部分或生成额外的内容,如元素的第一个字母、元素的第一行等
以双冒号(::)开头,后面跟着伪元素的名称,如::first-letter、::first-line等
可以在元素中生成额外的内容,并对其进行样式化
一般只能与元素选择器配合使用,不能与其他选择器(如类选择器、ID选择器)组合使用
通过flex布局
使用flex布局可以很方便地实现元素的垂直和水平居中,只需要在父容器上设置display:flex和justify-content:center,align-items:center(主轴方向)即可
元素不定宽高时垂直居中的方法
使用table-cell布局同样可以实现元素的垂直和水平居中
ps:需要在父容器上加上display:table来使display:table-cell生效
使用grid布局
可以将display:flex改写成 display:grid,并添加place-items:center
使用绝对定位和transform属性
通过将子元素的position属性设置为absolute,left和top值都为50%,然后通过transform:translate函数将其往左和往上移动子元素宽高一半的距离
Margin和绝对定位 四个值为0 margin:0 auto
回流:
当渲染树中部分或者全部元素的尺寸、结构或者属性发生变化时,浏览器会重新渲染部分或者全部文档的过程就称为回流。
触发场景:
页面的首次渲染;
浏览器的窗口大小发生变化;
元素的内容发生变化;
元素的尺寸或者位置发生变化;
元素的字体大小发生变化;
激活CSS伪类;
查询某些属性或者调用某些方法;
添加或者删除可见的DOM元素;
在触发回流重排的时候,由于浏览器渲染页面是基于流失布局的,所以当触发回流时,会导致周围的DOM元素重新排列,它的影响范围有两种:
全局范围:从根节点开始,对整个渲染树进行重新布局
局部范围:对渲染树的某个部分或者一个渲染对象进行重新布局
浏览器渲染页面的流程?回答蓝色字
构建DOM树:浏览器从服务器获取HTML文档,并解析HTML代码,构建DOM树结构。DOM树表示文档的结构和层次关系
构建CSSOM树:浏览器解析CSS样式表,构建CSSOM树。CSSOM表示文档的样式信息
合并DOM树和CSSOM树:将DOM树和CSSOM树合并成一个渲染树,渲染树只包含需要显示的文档节点和其样式信息
布局计算:根据渲染树的节点和其样式信息,进行布局计算,确定每个节点在页面中的位置和大小
绘制页面:根据布局计算的结果,使用绘图命令将页面的内容绘制到屏幕上
栅格化:即那个页面划分为一个一个小的矩形区域,称为栅格,然后将栅格中的内容转换为位图
合成和显示:将位图发送给GPU进行合成,最终显示在屏幕上
在这个过程中,浏览器还会进行优化措施,如异步加载资源、缓存机制、样式预处理等,以提高页面的加载速度和渲染性能。
需要注意的是,以上流程是一个简化的描述,实际中还会涉及到一些细节和优化策略,例如重绘和回流的优化、异步加载脚本的执行等。不同浏览器可能有一些差异,但总体流程是相似的。
当触发回流时,一定会触发重绘,但是重绘不一定会引发回流
transform会触发重绘和回流吗?
transform属性可以应用于元素来进行旋转、缩放、平移或倾斜德恩变换效果,而不会引起重绘和回流
使用transform属性进行变换时,浏览器会将元素的渲染层与其他层分离,将变换应用于独立的“合成层”,而不会影响其他元素的布局和渲染。这种独立的渲染过程不会触发重绘和回流,因此transform属性对于性能优化非常有用。
然而,值得注意的是,如果在
transform 属性之后的操作(如修改元素的尺寸或位置)需要触发回流的话,会导致浏览器重新计算元素布局,从而可能引起回流。因此,在使用
transform 属性时,最好避免与具有回流效果的操作同时进行。
transform原理?
是通过创建一个变换矩阵来实现元素的变化。变化矩阵是一个33或44的矩阵,其中包含了旋转、缩放、平移和倾斜等变换的参数
重绘:
当页面中某些元素的样式发生变化,但是不会影响其在文档流中的位置时,浏览器就会对元素进行重新绘制,这个过程就是重绘。
触发场景:
color、background相关属性:background-color、background-image等
outline相关属性:outline-color、outline-width、text-decoration
border-radius、visibility、box-shadow
PS:当触发回流时,一定会触发重绘,但是重绘不一定会引发回流
减少回流与重绘的措施:
操作DOM时,尽量在低层级的DOM节点进行操作;
不要使用table布局,一个小的改动可能会使整个table进行重新布局;
使用CSS的表达式;
不要频繁操作元素的样式,对于静态页面,可以修改类名,而不是样式;
使用absolute或者fixed,使元素脱离文档流,这样他们发生变化就不会影响其他元素;
避免频繁操作DOM,可以创建一个文档片段documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中
将元素先设置display:none,操作结束后再把它显示出来。因为在display属性为none的元素上进行的DOM操作不会引发回流和重绘。
将DOM的多个读操作(或者写操作)放在一起,而不是读写操作穿插着写。这得益于浏览器的渲染队列机制
响应式设计是一个网站能够兼容多个终端,而不是为每一个终端做一个特定的版本;
页面的设计与开发应当根据用户行为以及设备环境(系统平台,屏幕尺寸,屏幕定向等)进行相应的响应和调整
关于原理:
基本原理是通过媒体查询(@media)查询检测不同的设备屏幕尺寸做处理,页面头部必须有meta声明viewport
如何做?
媒体查询
百分比
百分比布局缺陷:
依赖于父元素的尺寸:百分比布局的子元素尺寸是相对于父元素的尺寸来计算的,因此,如果父元素的尺寸不稳定或无法确定,子元素的布局可能会受到影响。这可能导致在某些情况下布局出现混乱或不符合预期
对于复杂布局的限制:百分比布局适用于相对简单的布局场景,当对于复杂的布局需求,例如多列布局或嵌套布局,百分比布局可能变得复杂且难以管理。在这些情况下,使用其他布局技术(如弹性布局或网格布局)可能更加灵活和方便
对于固定大小元素的限制:百分比布局适用于自适应布局,但对于具有固定大小的元素,如背景图像或像素精确的UI元素,百分比布局可能不适用。在这些情况下,需要使用其他单位(如像素或rem)来确保元素的准确尺寸
兼容性问题:尽管百分比布局在现代浏览器中得到广泛支持,但在某些旧版本的浏览器中可能存在兼容性问题。在设计百分比布局时,需要进行兼容性测试,并根据需要提供备用方案或使用其他布局技术
vw/vh
rem
实现rem布局
设置的字体大小,在css中,rem单位是相对于根元素(即)的字体大小来计算的。因此,首先需要设置元素的字体大小。
使用rem单位进行布局:使用rem单位来替代传统的像素单位进行布局。1rem等于根元素的字体大小。
例如,如果要设置一个元素的宽度为根元素字体大小的2倍,可以这样写:
使用rem单位进行布局时,所有的尺寸都将根据根元素的字体大小进行相对调整。
响应式布局:通过设置根元素的字体大小,可以实现响应式布局。随着浏览器窗口大小的变化,根元素的字体大小也会相应调整,从而影响整个页面的布局。
.my-element {
width: 2rem;
}
例如,可以使用媒体查询(media queries)来根据不同的屏幕宽度设置不同的根元素字体大小,以实现响应式布局:
@media (max-width: 768px) {
html {
font-size: 14px;
}
}
@media (min-width: 769px) and (max-width: 1024px) {
html {
font-size: 16px;
}
}
@media (min-width: 1025px) {
html {
font-size: 18px;
}
}
js实现rem的实现方式
a. 获取根据根元素的字体大小:在JavaScript中,可以使用document.documentElement来获取根元素(即HTML),然后使用getComputedStyle方法获取其计算后的样式。从中可以提取出根元素的字体大小
var rootFontSize = parseFloat(getComputedStyle(document.documentElement).fontSize);
b. 设置rem单位的值:将需要使用rem单位的元素的尺寸除以根元素的字体大小,即可得到使用rem单位的值
var elementWidth = 200; // 假设需要设置的元素宽度为200px
var remWidth = elementWidth / rootFontSize + ‘rem’;
c. 动态设置样式:使用JavaScript动态设置元素的样式,将计算得到的rem值赋给相应的属性
var element = document.getElementById(‘my-element’); // 假设需要设置样式的元素的ID为’my-element’
element.style.width = remWidth;
减少选择器的复杂性:过于复杂的选择器会增强CS解析和匹配的时间。尽量使用简单的选择器,并避免使用通配符(*)选择器和嵌套选择器。
避免使用昂贵的属性和值:某些CSS属性和值的计算成本较高,例如使用box-shadow、text-shadow等。在性能敏感的情况下,尽量避免使用这些属性和值。
压缩和合并CSS文件:将多个CSS文件合并为一个文件,可以减少HTTP请求的数量,并通过压缩CSS文件大小来加快加载速度。
使用缩写属性:使用缩写属性可以减少CSS文件大小,并降低解析时间。例如,使用margin和padding的缩写属性,可以将四个方向的值合并为一个值。
避免使用!important:过多地使用!important会增加CSS解析的复杂性,尽量避免使用它,除非确实需要覆盖特定的样式。
避免使用CSS表达式:CSS表达式是一种强大但性能较差的特性,应尽量避免使用。通常可以使用其他方式来实现相同的效果。
使用CSS预处理器:CSS预处理器如Sass和Less提供了更高级的功能和语法,可以帮助提高CSS的可维护性和性能。预处理器可以将CSS代码编译为最终的优化版本。
使用媒体查询:使用媒体查询可以根据设备的特性和屏幕尺寸动态加载不同的CSS样式,从而避免不必要的样式加载和渲染。
延迟加载CSS:将不影响首次渲染的CSS代码延迟加载,可以加快页面的加载速度。可以使用异步加载、懒加载或通过JavaScript动态加载CSS。
使用CSS动画和过渡效果:CSS动画和过渡效果通常比使用JavaScript实现的动画性能更好。尽量使用CSS动画和过渡来提高性能和流畅度。
作为一名前端工程师,主要负责开发和维护网页前端部分,即用户在浏览器中直接交互的界面。前端工程师需要熟悉 HTML、CSS 和 JavaScript 等前端技术,掌握相关的框架和工具,如 React、Vue.js、Webpack 等。同时,前端工程师还需要具备良好的设计和用户体验意识,能够将设计师提供的视觉设计转化为交互流畅、用户友好的网页界面。
前端工程师的主要职责包括:
实现网页前端界面:将设计师提供的视觉设计转化为网页界面,使用 HTML、CSS 和 JavaScript 等技术进行开发。
优化用户体验:关注用户体验,提升网页的加载速度和响应性能,确保用户能够顺畅地浏览和操作网页。
跨平台和跨浏览器兼容性:确保网页在不同的浏览器和设备上都能正常显示和运行,优化兼容性。
与后端接口对接:与后端开发人员进行合作,对接后端接口,实现数据的获取和交互。
前端工程师的前景非常广阔。随着互联网的快速发展,越来越多的企业和组织需要开发和维护网站和网页应用程序。同时,移动互联网的普及和技术的进步,也催生了大量的移动端和响应式网页的需求。前端工程师的技术栈和能力要求也在不断扩展,例如移动端开发、PWA(Progressive Web App)、前端性能优化、前端框架和工具的使用等等。
未来,前端工程师的前景依然较为乐观。随着云计算、人工智能、物联网等技术的快速发展,前端工程师有机会参与更多领域的开发,如大数据可视化、人机交互界面、虚拟和增强现实应用等。同时,前端工程师的职位也与设计、产品、运营等职能有着密切的联系,具备良好的团队合作和沟通能力可以在职场中获得更多的机会。总体来说,前端工程师的岗位前景较为广阔,并且持续不断地更新和进化。
基本数据类型:
数字number用于表示数值,包括整数和浮点数
字符串 string:用于表示文本数据,使用单引号或双引号阔气来
布尔值Boolean:表示真或假
空值null:表示一个空的活不存在的值
未定义undefined:表示未定义的值
符号symbol:表示唯一的表示符。这些基本数据类型在存储上都是以固定大小的方式存储,他们是不可变的,也就是说,每次对其进行操作时,都会创建一个新的值。
复杂数据类型:
对象object:表示对个值的集合,可以存储键值对。对象可以存储任意类型的数据,并且可以通过键访问值。对象是引用类型,存储的是指向实际数据的引用,而不是数据本身。这意味着当多个变量引用同一个对象时,他们实际上引用的是同一个对象
数组array:表示一个有序的集合,可以存储多个值。数组可以存储任意类型的数据,并且可以通过索引访问值。数组也是引用类型,存储的是指向实际数据的引用。
在内存中,基本数据类型的值会直接存储在变量所分配的内存空间中。而引用类型的值(对象和数组)存储在堆内存中。变量实际上存储的是在指向堆内存中实际数据的地址
当复制基本数据类型的值时,会将值复制到新的变量中。而复制引用类型的值时,实际上只是复制了指向堆内存中数据的地址,多个变量引用的是同一个对象或数组。
存储差别:
基本数据类型在内存中占据固定大小的空间,退出直接存储在栈中
复杂数据类型的大小不固定,通常存储在堆中,而在栈中存储的是指向堆内存地址的引用
基本数据类型的存储是按值进行的,每个变量都有自己的独立存储空间,他们之间互不影响。当一个变量的值被赋给另一个变量时,会创建一个新的副本。
复杂数据类型的存储是按引用进行的。当复杂数据类型赋值给一个变量时,实际上是将内存中的引用地址赋给了变量,而不是复制整个数据。因此,多个变量可以引用同一个对象,对其中一个变量的修改会影响到其他引用了相同对象的变量。
两者都是JavaScript中用于检查数据类型的运算符,typeof无法判断对象和数组还有null因为返回的都是object
instanceof 检测构造函数的prototype属性是否出现在某个实例对象的原型链上,用来区分自定义对象类型返回一个布尔值,不能直接判断类型,因为实例是一个对象或函数创建的是引用类型 所以需要通过基本类型对应的包装对象来判断所以对于null和undefined就检测不了
区别:
typeof运算符用于检查一个值的数据类型,返回一个表示数据类型的字符串;typeof运算符适用于检查基本数据类型、函数和未定义的变量
instanceof运算符用于检查一个对象是否是某个特定类的实例。它检查一个对象的原型链,如果原型链中存在指定类的构造函数,则返回true,否则返回false。
总结:
typeof用于检查数据类型,返回一个字符串,适用于基本数据类型、函数和未定义的变量
instanceof 用于检查对象是否为某个特定类的实例,返回一个布尔值,适用于对象类型。
如果想要通过检测数据类型还可以使用Object.prototype.toString.call()会返回一个[object xxx]的字符串(xxx就是类型)但是不能准确判断是否是实例,也就无法区分对象类型
Array.isArray()是一个静态方法 判断是否为一个数组 返回一个布尔值
闭包是指在一个函数内部创建的函数,该函数可以访问并持有外部函数的变量,即使外部函数已经执行完毕,这个函数依然可以访问到那些变量。换句话说,闭包可以使函数访问其词法作用域之外的变量。换句话说,闭包可以使函数访问其词法作用域之外的变量。
闭包可以让你在一个内层函数中访问到其外层函数的作用域 , 也可以说是函数 + 上下文调用。
闭包的优缺点:
优点:可以重复使用变量,避免全局变量的污染和私有变量的存在
缺点:常驻内存增大内存的使用量,使用不当会造成内存泄漏。
闭包会使函数中的变量都被保存在内存中,内存消耗很大,不能滥用
使用场景:
保护变量:闭包可以用于创建私有变量,将变量封装在函数内部,外部无法直接访问和修改,只能通过闭包提供的接口来操作变量
计数器和累加器:闭包可以用于创建计数器和累加器,通过闭包持有一个变量,并提供增加或减少的方法,每次调用方法都可以得到变量的更新结果
延迟执行和回调:闭包可以用于创建延迟执行的函数,将需要延迟执行的代码包裹在闭包中,并设置定时器,从而实现延迟执行的效果。闭包还可以用于实现回调函数,将函数传递给其他函数,在合适的时机调用。
模块化开发:闭包可以用于创建模块化的代码结构,通过闭包可以将一组相关的函数和变量封装在一个作用域内,避免全局变量污染和命名冲突
创建闭包的最常见的方式是在一个函数内创建另一个函数,创建的函数可以访问到当前函数的局部变量
两个常用的用途:
使我们在函数外部访问到函数内部的变量。通过使用闭包,可以通过在外部调用闭包函数,从而在外部访问到函数内部的变量,可以使用这种方法来创建私有变量
使已经运行结束的函数上下文中的变量对象继续留在内存中,因为闭包函数暴露了这个变量对象的引用,所以这个变量对象不会被回收
垃圾回收机制和内存泄露?
导致内存泄露的原因:
意外的全局变量:由于使用未声明的变量,而意外的创建了一个全局变量,而使这个变量一直留在内存中无法被回收
被遗忘的计时器或回调函数:设置了setInterval定时器,而忘记取消它,如果循环函数有对外部变量的引用的话,那么这个变量会被一直留在内存中,而无法被回收
脱离DOM的引用:获取一个DOM元素的引用,而后面这个元素被删除,由于一直保留对这个元素的引用,所以它也无法被回收。
闭包:不合理的使用闭包,从而导致某些变量一直被留在内存当中。
垃圾回收概念:
JavaScript代码运行时,需要分配内存空间来存储变量和值。当变量不在参与运行时,就需要系统收回被占用的内存空间
回收机制:
JavaScript具有自动回收机制,会定期对哪些不再使用的变量、对象所占用的内存进行释放,原理就是找到不再使用的变量,然后释放掉其所占用的内存
JavaScript中存在两种变量:局部变量和全局变量。全局变量的生命周期会持续要页面卸载;而局部变量声明在函数中,它的生命周期从函数执行开始,直到函数执行结束,在这个过程中,局部变量会在堆或栈中存储他们的值,当函数执行结束后,这些局部变量不再使用,他们所占的空间就会被释放
不过,当局部变量被外部函数使用时,其中一种情况就是闭包,在函数执行结束后,函数外部的变量依然指向函数内部的局部变量,此时局部变量依然在被使用,所以不会回收
垃圾回收的方式:
浏览器通常使用的垃圾回收方法有两种:标记清除,引用计数
标记清除
引用计数
减少垃圾回收
对数组进行优化:
对object进行优化:对象尽量复用,对于不再使用的对象,就将其设置为null,尽快被回收
对函数进行优化:在循环中的函数表达式,如果可以复用,尽量放在函数的外面
循环中使用闭包解决var定义函数的问题?
for (var i = 1; i <= 5;i++){
setTimeout(function timer() {
console.log(i)
},i1000)
}
首先因为setTimeout是一个异步函数,所以会先把循环全部执行完毕,这时候i=6,所以会输出一堆6。三种解决方法
for (var i = 1; i <= 5;i++){
;(function (j){
setTimeout(function timer() {
console.log(i)
},i1000)
})(i)
}
在上述代码中,首先使用了立即执行函数将i传入函数内部,这个时候值就被固定在了参数j上面不会该笔那,当下次执行timer这个闭包的时候,就可以使用外部函数的变量j,从而达到目的。
使用setTimeout的第三个参数,这个参数会被当成timer函数的参数传入。
使用let定义i来解决问题
词法作用域是什么,以及它的特点?
也称为静态作用域,是指在代码编写阶段确定变量和函数作用域的规则。它是根据变量和函数在源代码中的位置来确定其作用域的。
在词法作用域中,作用域是由代码的结构和嵌套关系决定的,而不是由代码的执行过程决定的。当代码嵌套时,内部作用域可以访问外部作用域中定义的变量,而外部作用域无法访问内部作用域中定义的变量
这个作用域的形成是因为在词法分析阶段,编译器就已经确定了每个变量在哪个作用域中被声明,当代码执行时,根据作用域链来查找变量的值。
特点:
变量的作用域在代码编写阶段就已经确定,不会受到函数的调用顺序影响
内部作用域可以访问外部作用域中的变量,但外部作用域无法访问内部作用域中的变量
变量的查找是沿着作用域链向上查找的,知道找到第一个匹配的变量
在js中,所有的函数再被调用的时候都会默认传入两个参数,一个是this,还有一个是arguments。在默认情况下this都是指当前的调用函数的对象。但是有时候我们需要改变this的指向,我们可以使用如下方法:
使用ES6中箭头函数
函数内部使用_this=this
使用这三种方法
new实例化一点对象
call,apply和bind都是Function原型中的方法,而所有的函数都是Function的实例
call和apply的作用一模一样,区别仅在于传入的参数形式不同。
apply接收两个参数,第一个参数指定了函数体内this对象的指向,第二个参数为一个带下标的集合,这个集合可以为数组,也可以是伪类数组,apply方法把这个集合中的元素作为参数传递给被调用的函数
call传入的参数数量不固定,跟apply相同的是,第一个参数也是一个代表函数体内的this指向,从第二个参数开始往后,每个参数被依次传入函数
bind方法通过传入一个对象,返回一个this绑定了传入对象的新函数,这个函数的this指向除了使用new时会被改变,其他情况下都不会改变
实现bind步骤:
判断调用对象是否为函数,即使是定义在函数的原型上的但是可能出现使用call等方式调用的情况
保存当前函数的引用,获取其余传入参数值
创建一个函数返回
函数内部使用apply来绑定函数调用,需要判断函数作为构造函数的情况,这个时候需要传入当前函数的this给apply调用,其余情况都传入指定的上下文对象
Function.prototype.myBind = function (context){
if(typeof this !== ‘function’){
throw new TypeError(‘Error’)
}
var args = […arguments].slice(1),
fn = this;
return function Fn() {
return fn.apply(
this instanceof Fn ? this :context,
args.concat(…arguments)
)
}
}
事件循环是JavaScript引擎处理异步操作的机制。它是一种用于协调和处理事件、回调函数和其他异步操作的方式。
在JavaScript中,通常使用异步操作来处理耗时的任务,如网络请求、文件读写、定时器等。而为了保证主线程的响应性,避免阻塞,JavaScript引擎采用了事件循环的机制。
事件循环的基本原理如下:
执行同步代码:首先,JavaScript引擎会执行当前线程上的同步任务,这些任务按照代码的顺序依次执行。
处理异步任务:当遇到异步任务(如回调函数、定时器回调等)时,JavaScript引擎会将这些任务交给相应的web API(如DOM API、定时器API等)处理,并立即返回主线程继续执行后续的同步任务
将完成的任务添加到任务队列:当异步任务完成时,它会被添加到任务队列中
事件循环:当主线程的同步任务执行完毕,JavaScript引擎会检查任务队列,如果队列中有任务,就会将其中的任务依次取出,并将其相关的回调函数放入主线程执行。
这个过程会不断重复,从而形成了事件循环。事件循环使得JavaScript能够处理大量的异步操作,而不会阻塞主线程,保证了JavaScript的非阻塞特性。
需要注意的是,事件循环中的任务队列分为宏任务(macrotask)和微任务(microtask)两种类型。宏任务包括 I/O 操作、定时器回调等,而微任务包括 Promise 回调、MutationObserver 回调等。微任务的执行优先级高于宏任务,在每次事件循环的过程中,会先清空微任务队列,然后再执行宏任务。这种机制可以保证微任务的及时执行,提高页面响应速度和用户体验。
总结起来,事件循环是 JavaScript 处理异步操作的核心机制,通过事件循环,JavaScript 引擎可以高效地处理各种异步任务,并保持主线程的响应性。
使用场景:
用户交互事件:当用户在网页上进行点击等操作时,这些事件会被捕捉并加入到事件队列中。事件循环会在适当的时机将这些事件处理,并更新相应的用户界面
网络请求:在进行异步网络请求时,事件循环可以通过回调函数的方式处理请求的响应。当响应返回时,事件循环会调用相应的回调函数来处理返回的数据。
定时器:事件循环可以管理定时器,通过设置定时器来执行一些延时操作。当定时器到期时,事件循环会将相应的回调函数加入事件队列,以便在合适的时机执行。
异步操作:事件循环可以管理定时器,通过设置定时器来执行一些延时操作。当定时器到期时,事件循环会将相应的回调函数加入事件队列,以便在合适的时机执行。
优势:
在于可以实现非阻塞的异步操作,提高程序的性能和响应能力,使得JavaScript可以处理大量的并发操作,而不需要等待每个操作的完成。
在JavaScript中的作用域有哪些?
全局作用域,函数作用域,块级作用域,模块作用域
全局作用域:
最外层函数和最外层函数外面定义的变量拥有全局作用域;
所有未定义直接赋值的变量自动声明为全局作用域;
所有window对象的属性拥有全局作用域
全局作用域有很大的弊端,过多的全局作用域变量会污染全局命名空间,容易引起命名冲突
函数作用域:
声明在函数内部的变量,一般那只有固定的代码片段可以访问到;
作用域是分层的,内部作用域可以访问外部作用域,反之不行
块级作用域:
使用ES6中新增的let和const指令可以声明块级作用域,苦自己作用域可以在函数中创建也可以在一个代码块中出创建(由{}包裹的代码片段)
let和const声明的变量不会有变量提升,也不可以重复声明
在循环中比较适合绑定块级作用域,这样就可以把声明的计数器变量限制在循环内部
作用域链:
在当前作用域中查找所需变量,但是该作用域没有这个变量,那这个变量就是自由变量。如果在自己作用域找不到该变量就去父级作用域查找,依次向上级作用域查找,直到访问到window对象就被终止,这一层层的关系就是作用域链。
作用:
保证对执行环境有权访问的所有变量和函数的有序访问,通过作用域链,可以访问到外层环境的变量和函数
本质:
是一个变量对象的指针列表。变量对象是一个包含了执行环境中所有变量和函数的对象。
this是执行上下文中的一个属性,它指向最后一次调用这个方法的对象。在实际开发中,this的指向可以通过四种调用模式来判断
函数调用模式,当一个函数不是一个对象的属性时,直接作为函数来调用时,this指向全局对象。
方法调用模式,如果一个函数作为一个对象的方法来调用时,this指向这个对象
构造器调用模式,如果一个函数用new调用时,函数执行前会新创建一个对象,this指向这个新创建的对象
apply、call和bind调用模式,这三个方法都可以显示的指定调用函数的this指向
其中 apply方法接收两个参数:一个是 this 绑定的对象,一个是参数数组。call 方法接收的参数,第一个是 this 绑定的对象,后面的其余参数是传入函数执行的参数。也就是说,在使用 call0 方法时,传递给函数的参数必须逐个列举出来。bind 方法通过传入一个对象,返回一个 this 绑定了传入对象的新函数。这个函数的 this 指向除了使用 new 时会被改变,其他情况下都不会改变。
首先创建了一个新的空对象
设置原型,将对象的原型设置为函数的prototype对象
让函数的this指向这个对象,执行构造函数的代码(为这个新对象添加属性)
判断函数的返回值类型,如果是值类型,防护创建的对象。如果是引用类型,就返回这个引用类型的对象
首先两者都是基本数据类型,这两个基本数据类型分别都只有一个值,就是undefined和null
undefined代表的含义是未定义,null代表的含义是空对象。一般变量声明了但还没有定义的时候会返回undefined,null主要用于赋值给一些可能会返回对象的变量,作为初始化。
undefined在JavaScript中不是一个保留字,这意味着可以使用undefined来作为一个变量名,但是这样的做法是非常危险的,它会影响对undefined值的判断。我们可以通过一些方法获得安全的undefined值,比如说void 0。
use strict 是一种ECMAscript5添加的(严格模式)运行模式,这种模式使得JavaScript在更严格的条件下运行。设立严格模式的目的如下:
消除JavaScript语法的不合理、不严谨之处,减少怪异行为;
消除代码运行的不安全之处,保证代码运行的安全;
提高编译器效率,增加运行速度;
为未来新版本的JavaScript做好铺垫
区别:
禁止使用with语句
禁止this关键字指向全局对象
对象不能有重名的属性
箭头函数比普通函数更加简洁
箭头函数没有自己的this
箭头函数继承来的this指向永远不会改变
call、apply、bind等方法不能改变箭头函数中this的指向
箭头函数不能作为构造函数使用
箭头函数没有自己的arguments对象,在箭头函数中访问arguments实际上获得到的是它外层函数的arguments
箭头函数没有prototype
箭头函数不能用作Generator函数,不能使用yeild关键字