10.6
1.构造函数中的方法应该写在哪里?
- 属性写在构造函数中,对象中的方法应该写在prototype属性上
- 原因:创建多个实例时不会每个实例都在内存中创建方法,节省内存空间
- 当实例想要调用构造函数中的方法,会自动去原型链的prototype属性找
2.原型链的终点?
- Object.prototype是原型链的终点
- Object.prototype属性内置了.hasOwnProperty()和.toString()方法,所以我们创建的实例就能够通过原型链调用到这两个方法。
3.数组的原型链?
- 数组能够调用.push()/.pop()/.slice()等方法就是因为这些方法定义在了Array构造函数的prototype属性上
- 而数组的.isArray()方法则需要Array去调用,这是因为这个方法没有定义到prototype属性上,而是构造函数里
- 而数组能够调用.toString()方法,就是因为array的原型Object.prototype
4.什么是继承?
- 继承描述了两个类之间的“is a kind of”关系,比如学生“是一种人”,所以学生与人之间就构成了继承关系
- People称为父类(或者超类,基类),Student被称为子类(派生类)
- 子类有父类的所有特性,并丰富了父类,让类描述的更加具体,更加细化
5.javaScript中如何实现继承?
- 实现继承的关键在于:子类必须父类全部的属性和方法并且子类拥有自己特有的属性和方法
- 使用javaScript特有的原型链特性来实现继承,是普遍的做法
- ES6中有新的实现继承的方法
- 总结:子类的原型指向父类的实例
6.实现继承的步骤?
- 首先创建父类,在prototype属性中书写方法,再创建一个子类,子类要拥有父类的全部属性
- 让子类的prototype属性指向父类new出来的一个对象
- 子类的实例就可以调用父类的方法,实现类继承
- 子类可以重写父类的重名方法(override),主要应用到了构造函数的遮蔽效应
7.面向对象的本质是什么?
- 定义不同的类,让类的实例去工作。
- 面向对象最重要的工作就是去编写类
8.面向对象的优点是什么?
- 程序编写更加清晰,代码结构更加严密,使代码更健壮更利于维护。
9.面向对象经常用到的场合?
10.7
1.什么是包装类?
- Number()、String()和Boolean()分别是数字、字符串、布尔值的包装类
- 包装类的目的就是让基本类型变量,可以从他们的prototype属性上获得方法
- 用new包装类得到的基本类型变量的类型是object
2.包装类的作用?:包装类的作用就是能够使基本类型变量通过原型链来调用包装类的prototype存在的方法,比如:
<script>
var a=123;
var b=new Number(123);
a.toString();
</script>
3.包装类总结?
- Number()、String()和boolean()的new实例都是object类型,他们的PrimitiveValue属性存储他们本身值
- new出来的基本类型值可以正常参与运算
- 包装类的目的就是为了让基本类型值可以从他们的构造函数的prototype上获得方法
- 包装类指的是对基本类型变量的封装,引用类型的封装不能称为包装类
- undefined和null没有包装类
4.Math对象常用方法?
- .pow(2,3):幂次方,求2的3次方
- .sqrt(5):开根号,求根号5
- .ceil(2.1):向上取整,结果为3
- .floor(2.9):向下取整,结果为2
- .round():四舍五入
- .round(2.5):3
- .round(-2.5):-2,注意,使用round方法,当判断值为负数时,则以.5为临界点,只要小于等于.5则舍,大于.5则入。
- .max():得到参数列表的最大值
- .min():得到参数列表的最小值
- .random():随机数生成0-1之间的随机小数,不会生成1
- .abs():取绝对值
- .PI():取PI值
5.如何将一个小数四舍五入到指定位数?
- 以小数点后两位为例,先将小数点向右移2位,就是乘以100
- 再进行四舍五入,得到后的结果,再除以100
- 得到最终的结果
6.如何求一个数组的最大值?
<script>
var a=123;
a.toString();
var arr=[12,14,15,17,18];
console.log(Math.max.apply(null,arr));
console.log(Math.max(...arr));
</script>
7.获取Date日期对象的方法有哪些?
- 使用new Date()可以获得当前时间的日期对象,它是object类型值
- 使用new Date(2020,11,1):获取指定时间的日期对象,这种写法不算时区
- 第二个参数表示月份,从0开始计算,11就表示12月
- 也可以使用new Date(“2020-12-01”)这样的写法,这种写法算时区,中国属于东八区
- 要注意月和日要是两位数字,不足两位的向前边补0
- 这里的月写几就是几,从1开始计算,12就表示12月
8.日期对象的常见几个方法?
date日期对象:14个方法:
- 八个,获取年月日时分秒毫秒星期等单个数据的方法:
- .getFullYear():得到年份
- .getMonth():得到月份0-11,获取的月份,比真实月份少1
- .getDate():得到日期1-31
- .getHours():得到小时数0-23
- .getMinutes():得到分钟数0-59
- .getSeconds():得到秒数0-59
- .getMilliSeconds():得到毫秒0-999
- .getDay():得到星期0-6,0代表星期日,1-6代表星期1-6
- 三个,获取日期时间字符串的方法:
- .toLocaleString():获取本地的日期时间字符串
- .toLocaleDateString():获取本地的日期字符串
- .toLocaleTimeString():获取本地的时间字符串
- 两个,将日期对象转为时间戳的方法:
- Date.prase():将日期对象转为时间戳,精确到秒
- .getTime():转为时间戳,精确到毫秒
- 一个:时间戳转为时间对象的方法:
- new Date(时间戳):时间戳转为时间对象
9.什么是时间戳?
- 时间戳表示1970年1月1日距离某时刻的毫秒数
- 通过.getTime()方法或者Date.parse()方法可以将日期对象变为时间戳
- 通过new Date(时间戳)的写法,可以将时间戳转为日期对象
<script>
var d=new Date();
console.log(d.getTime());
console.log(Date.parse(d));
console.log(new Date(d.getTime()));
</script>
10.什么是正则表达式?
11.正则表达式如何创建?
- 方式一:使用/内容/的形式创建一个正则表达式
- 方式二:使用new RegExp(‘内容’)的形式创建一个正则表达式,但是需要注意转义字符\
- js内置了RegExp()构造函数
- 使用typeof运算符检测正则表达式的类型,结果是object
12.什么是元字符?使用元字符需要注意什么?
var regExp=/^.$/;
var regExp=/^\.$/;
- 不管一个符号是不是特殊字符,都可以在这个符号之前加一个\以确保它表达的是这个符号本身
13.方括号表示法的作用是什么?更高级的用法?
14.什么是量词?
量词:表示前面表达式出现次数的词
- *:匹配前一个表达式0次或者多次,等价于{0,}
- +:匹配前一个表达式1次或者多次,等价于{1,}
- ?:匹配前面一个表达式0次或者1次,等价于{0,1}
- {n}:匹配前面一个表达式刚好出现n次
- {n,}:匹配前面一个表达式至少出现n次
- {n,m}:匹配前面一个表达式最少出现n次,最多出现m次
15.什么是正则表达式的修饰符?
10.8
1.正则表达式可以“打点”调用哪些方法?
var reg=/0-9a-z/;
2.字符串有哪些方法可以用正则表达式?
var str=“ascasd”;
var reg=/[a-z]/;
- str.search(reg):在字符串中根据正则表达式进行查找匹配,返回首次匹配到的位置索引,不存在则返回-1
- str.match(reg):在字符串中根据正则表达式进行查找,返回一个所有符合条件的字符串数组,找不到返回null
- str.replace(reg,“新内容”):使用替换字符串替换掉匹配到的子字符串,可以使用正则表达式
- str.split(reg):分割字符串为数组,可以使用正则表达式
3.正则表达式的实际应用?
用正则表达式进行表单验证是正则表达式最重要的实际应用
4.onmouseover与onmouseenter的区别?
区别:
10.9
1.let和var和const的区别?
- var的变量可以多次声明,let如果声明已经存在的变量,就会报错
- var的变量会声明提升,let的变量也会声明提升,但是let的变量有一个它自己的暂时死亡区,在这个区域内不能调用它
- const是声明一个常量,必须赋初始值,且这个常量之后不能改变
10.10
1.setTimeOut和setInterval?
- setTimeOut是延时器,到时间后只执行一次
- setInterval是定时器,每隔指定时间就会执行一次
- 第一个参数是要执行的函数体
- 第二个参数是间隔时间毫秒数
- 从第三个参数开始,后面的参数都是要传到第一个函数里的实参
- 定时器和延时器都会有返回值,返回值是定时器和延时器在函数中是第几个出现的
2.setTimeOut时间设为0毫秒时,与它下面普通的代码执行顺序是什么?
- setTimeOut和setInterval属于异步代码,普通代码属于同步代码
- 编译器会先执行所有的同步代码,遇到异步代码,会先将它放到异步队列中
- 当所有的同步代码执行完成后,再开始执行异步队列中的异步代码
- 所以即使setTimeOut的延时时间设为0,也会先执行它下面的普通代码
- 用clear清定时器时,一般还会time=null;实现变量的垃圾回收
3.Date注意事项?
10.11
1.打开DOS命令窗口的几种方法?
- win+r–>输入cmd,
- 文件资源管理器路径上输入cmd,直接进入当前文件路径的cmd
- 安装git ->鼠标右键点击Bash Here
2.常用DOS命令?
DOS系统下使用,bash系统下有些不能用:
- 进入文件夹:
- cd:修改路径,进入
- cd …/:回退上一级
- cd /:快速回到根目录
- tab:遍历当前路径下的文件和文件夹
- cd tab键:遍历当前路径下的文件夹
- cd Desktop:跳转到桌面
- 创建:
- mkdir 文件夹名:创建文件夹
- copy con +文件名 ——>回车+输入内容——>ctrl+z->回车:创建一个文件夹
- 删除:
- 软删除:文件夹中有东西,删除不了
- rmdir 文件夹名:删除文件夹
- rd 文件夹名:删除文件夹
- 硬删除:文件夹中有东西也能删除,会提示是否删除
- 删除文件:
- 重命名:
3.git和svn?
- svn:集中式的版本控制系统
- git:分布式的版本控制系统
- 每一个电脑都属于一个完整的版本控制系统服务器
- 当多个电脑协同开发时,需要一个远程仓库(服务器)
- 每个电脑都可以将自己的版本代码提交到远程仓库
- 每个电脑都可以从远程仓库download下所有版本的代码
- 特点:
- 每个人的电脑就是一个服务器,有所有版本的信息
- 如果不需要协同开发,不需要联网也可以
- 传输是以文件流的形式传输的,比文件传输快
4.如何将a文件夹变成git仓库,将里面的文件生成版本?
git仓库分为三个区:工作区、暂存区、历史区
将文件生成git仓库,并生成仓库版本:
- 第一步:进入a文件夹,打开Bash Here
- 第二步:git init:初始化当前文件夹为git仓库
- 第三步:git status:查看当前工作区中文件的状态
- 红色:文件在工作区还没提交到暂存区
- 绿色:工作区的文件已经提交到暂存区了,但是还没有到历史区
- 第四步:git add +文件:将工作区的文件提交到暂存区
- git add .:将工作区所有文件全部提交到暂存区,不包含删除的
- git add A:将工作区所有文件全部提交到暂存区
- 第五步:git commit -m “注释”:提交a文件夹的当前版本到历史区
- 第六步:
- git log:查看历史区的所有版本,不包含回滚信息
- git reflog:查看所有历史版本信息,包括回滚信息
- clear:清除窗口代码
- git reset --hard 版本号:回退仓库的版本号(用git log命令查看,至少前七位)
在gitee上面创建仓库,并与git连接
mkdir zftest
cd zftest
git init
touch README.md
git add README.md
-------------上面的不重要-------------------
git commit -m "first commit";
git remote add origin https://gitee.com/aha-aha/zftest.git;//连接远程仓库
git remote -v://查看本地仓库关联的远程仓库
git remote remove origin;//删除远程连接
git push -u origin master;//向远程仓库push文件到主分支
cd “已经存在的仓库路径”
连接远程仓库:git remote add origin https://gitee.com/aha-aha/zftest.git
向远程仓库push文件到主分支:git push -u origin master
? -u origin master:表示推送到master分支
从gitee仓库中down下最新仓库到本地
5.分支如何操作?
6.什么是作用域,变量分几种,什么是作用域链,上级作用域怎样查找,闭包是什么?
https://www.jianshu.com/p/01804282372c
作用域: 表示一个变量的可用范围,避免不同范围的变量之间相互干扰。
作用域分为三种:
- 全局作用域:在浏览器端全局作用域就是window,他可以重复使用,随处可用。但是全局作用的定义的变量容易被污染
- 函数私有作用域:函数声明的时候会自动创建函数作用域对象scope,他指向函数声明时的作用域。每个函数调用时,在创建AO对象之前会先创建当前函数的作用域,并创建parent对象,通过函数声明时的作用域对象scope指向他函数的父级作用域。他不会污染全局,但是不会重复使用。函数执行完毕后会随着函数执行上下文出栈=>AO对象销毁而销毁
- es6新增的块级作用域:除了对象的{},其他的{}都算块级作用域,比如if(){};这个{}就是一个块级作用域,只对es6新增的变量类型有效,对var和function无效,块级作用域中的var和function还会声明提升
变量:
- 变量分为全局变量,局部变量
- 定义在函数外的是全局变量
- 定义在函数内的局部变量
- 形式参数也是局部变量
作用域链的查找机制:变量到创建这个变量的函数的作用域中取值。但是如果在当前作用域中没有查到值,就会向通过parent对象去父作用域去查,直到查到全局作用域,这么一个查找过程形成的链条就叫做作用域链。
- 如果全局作用域还没有这个变量
- 如果是查询:就报错:Uncaught ReferenceError:a is not defind
- 如果是不加变量类型的赋值:如a=100;,则是添加一个全局变量
上级作用域:上级作用域是谁,跟在哪执行没有任何关系,在哪定义的,它的上级作用域就是谁。
- 一定要与函数的上下文(this)区别开,函数的上下文是跟定义没关系,只跟谁调用了它有关。
**闭包:**函数执行形成新的私有作用域,里面的私有变量只在里面有效,与外面的毫无关系
7.想让js运行,需要提供环境?
- 浏览器(浏览器引擎)
- node(v8引擎)
- webview(v8引擎)
8.浏览器底层运行机制?
- 第一步:浏览器在运行js的时,会开辟两个内存空间,堆(heap)和栈(ECStack)
- 第二步:在堆中开辟一个空间地址,用于存放浏览器的一些方法和属性,这个对象称为GO
- 第三步:为了区分代码不同的作用域,在栈内存中会开辟两种作用域空间:全局执行上下文(EC(g))和私有执行上下文(EC(fn))
- 全局执行上下文**(EC(g))**:
- **功能:**存放全局变量区域VO、变量声明提升、全局代码自上而下的执行
- 私有执行上下文(EC(fn)):
- 每次调用函数都会在栈中生成一个独立的私有执行上下文
- **功能:**存放私有变量区域AO、局部变量声明提升、局部代码自上而下的执行
- 第四步:在全局执行上下文中创建存放全局变量的区域(VO),在私有执行上下文中创建存放局部变量的区域(AO)
- 第五步:在VO中创建window对象,并指向堆中的GO
9.用不同关键字声明的变量,他们在内存中都是如何创建的?
- ES5中声明变量的关键字:var 、function
- 全局变量:会创建成一个window对象的属性,属性名是变量名,属性值是变量值,因为window
- 局部变量:直接创建在私有函数上下文(EC(fn))的AO中
- ES6中声明变量的关键字:let 、const、class、import
- 全局变量:创建在VO中
- 局部变量:创建在EC(fn)的AO中
11.一段代码执行的过程详解?
- ECStack(Execution Context Stack):执行上下文栈
- heap:堆内存
- EC(g):全局执行上下文
- VO(Variable Object):变量对象(全局变量的存储区域)
- EC(fn):函数私有执行上下文
- AO(Activation Object ):活动对象 (函数的叫做AO,理解为VO的一个分支),私有变量的存储区域
- Scope:作用域,创建的函数的时候就赋予的
- Scope Chain :作用域链
- global object:全局对象
10.12
1.全局执行上下文和函数私有执行上下文?
- 全局执行上下文(EC(g)):js文件执行时生成
- 私有执行上下文(EC(fn)): 每次函数执行时都会生成独立的
2.如何判断某个对象是否拥有某个属性?
- 使用in关键字
- 语法:属性名 in 对象名
- 返回布尔值,如果有就返回true,否则返回false
3.在全局变量用var a=10;声明一个变量和a=10;声明一个变量的区别?
- 用var声明的变量会进行声明提升,直接a声明的变量不会进行声明提升
- var声明的变量用delete window.a的形式不能删除变量(不可配置的),直接a声明的变量用delete window.a的形式能够删除(可配置的)。
10.变量的声明提升?
var 声明的变量,只提升声明,赋值操作留在原地。- 函数声明提升整个函数体,函数表达式的提升行为和变量一致。
- 同作用域中,如果函数声明和
var 声明同名,只提升函数声明,忽略 var 声明。(var声明不会覆盖函数声明) let 和 const 有暂时性死区,必须先声明后使用。- 代码自上而下执行时:
- 执行到var 语句时,给变量赋值
- 执行到function语句时,由于开始已经声明提升了,所以function语句直接跳过,不再执行
4.变量提升需要注意的坑?
- 1、if语句:不管条件是否成立里面的变量都会声明提升
- var:只声明不定义
- 实名function:新版本:只声明不定义,旧版本:声明并且定义
- if条件成立后,首先将里面的实名函数进行定义的提升,再自上而下执行
- if里面的实名函数fn
- 无论条件是否成立,首先将fn作为变量名提升到if所在的作用域,这时全局fn=undefined
- 当条件成立进入if以后,首先再进行函数提升,提升if后面的代码块最顶部,形成局部函数fn=函数体,这时全局有一个fn,if代码块局部有一个fn
- 代码自上而下执行,代码块中对fn的操作都是操作的局部fn变量,以fn定义代码为分割线,当执行完fn定义代码后,会将局部fn变量的值提升到全局fn变量,这时全局fn变量就会被修改一次,fn定义代码后面对fn变量的操作,仍然是操作局部fn变量,全局fn不受影响。
- 2、函数表达式:提升var的变量,关键字不是var就不提升,函数表达式就是把等号后面的匿名函数看成变量的值
- 3、自执行函数:不声明提升,但自身形成的私有执行上下文里面的变量照常进行变量提升
- 4、return:return后面的代码(返回值代码)不会提升,return语句下面的代码如果有变量会声明提升
- 5、变量提升阶段:如果变量名重复,只声明一次,但会多次赋值(var提升不会覆盖同名的函数提升)
5.私有函数执行上下文里面都会有哪些操作?
- 第一步,生成AO私有变量存放区域
- 第二步:
- 初始化作用域链:scope->parent
- 初始化this指向:由函数的调用方式来确定
- 初始化arguments
- 给形参赋值
- 变量声明提升
- 第三步:代码自上而下执行
6.es6中的变量的特点?
- 一、es6中的变量,有变量提升,但是由于es6暂时性死区的作用,使得在执行到定义变量代码之前,不能调用该变量。
- 二、es6中的变量,阻断了与window的关系
- 三、es6中的变量,在同一作用域,不允许重复声明
- 五、es6中的变量,在不同的作用域,可以重复声明
- 私有作用域中只要定义了变量,就不会去找全局中的这个变量
- 但是这个变量不会声明提升,所以,不能在这个变量声明定义之前调用这个变量,变量声明定义之前的区域,成为它的暂时性死区,在暂时性死区访问变量会报错:Cannot access ‘a’ before initialization
- 四、es6中在代码自上而下执行之前有一个阶段:词法检测机制
7.什么是暂时性死区?
? 当程序的控制流程在新的作用域(module, function或block作用域)进行实例化时,在此作用域中的用let/const声明的变量会先在作用域中被创建出来,但因此时还未进行词法绑定,也就是对声明语句进行求值运算,所以是不能被访问的,访问就会抛出错误。所以在这运行流程一进入作用域创建变量,到变量开始可被访问之间的一段时间,就称之为TDZ(暂时死区)。
? 结论:let/const声明的变量,的确也是有提升(hoist)的作用。这个是很容易被误解的地方,实际上以let/const声明的变量也是会有提升(hoist)的作用。提升是JS语言中对于变量声明的基本特性,只是因为TDZ的作用,并不会像使用var来声明变量,只是会得到undefined而已,现在则是会直接抛出ReferenceError错误,而且很明显的这是一个在运行期间才会出现的错误。
? ES6 规定暂时性死区和let、const语句不出现变量提升,主要是为了减少运行时错误,防止在变量声明前就使用这个变量,从而导致意料之外的行为。这样的错误在 ES5 是很常见的,现在有了这种规定,避免此类错误就很容易啦~
8.arguments.callee和arguments.callee.caller的区别?
- arguments.callee:获取函数本身
- arguments.callee.caller:获取执行该函数的宿主函数,如果函数在全局下执行,则这个属性获取的值是null
- 这两个属性常用于匿名函数中
- 当在严格编码模式下(“use strict”),不能使用这两个函数
- 立即执行函数里可以匿名函数具名化:具名化后的函数变成常量,不能再重新赋值。
10.13
1.堆内存的销毁与栈内存的销毁机制?
堆内存:主要存放引用数据类型的真实数据
- 每个厂商的对堆内存中数据的销毁有不同的机制
- 谷歌:每过一段时间就会对没有被指向的数据空间进行垃圾回收(销毁)
- iE:对一个堆内存空间的指向个数计数,就是看有多少个引用这个数据的变量,当引用变量变为0时,就销毁这个内存空间
栈内存:存放变量,执行代码
- 栈内存的销毁机制:全局作用域的销毁、函数私有作用域的销毁
- 全局作用域的销毁:页面关闭时被销毁
- 函数私有作用域的销毁:执行完立即销毁、不销毁、不立即销毁。
2.栈内存中不销毁的作用域?
不销毁的作用域:函数里面有一个引用数据类型,被函数外界变量占用,导致这个函数不能被销毁,也被称为闭包
实质上:任何一个函数,只要它里面存在外界不能访问的私有变量,那么这个函数就形成了闭包
市面上:函数只有形成不销毁作用域,才称得上是闭包
3.不立即销毁的作用域(颗粒化函数)?
不立即销毁:当一个fn1函数中return了另一个fn2函数,通过fn1(1)(2),的形式调用这个函数时,fn1(1)执行结束后,由于返回的fn2函数还会被执行,所以fn1在栈内存中生成的私有执行上下文不会被立即销毁,而是等fn2()函数执行结束后fn1和fn2都被销毁。
4.闭包的作用?
- 保护:里面的私有变量不受干扰
- 保存:形成不被销毁的作用域,
5.当多人协同开发时,如何避免多个js文件之间的变量不被污染?
将自己的js文件所有的代码都用自执行函数包起来。
原因:使自执行函数生成私有作用域,当里面的函数找不到变量时,先去自执行函数的私有作用域去寻找,而不是去全局作用域寻找
目的:保护单个js文件里面的私有变量不受外界干扰。
6.如何实现js文件方法的初始化和封装?
- ES6新规则,如果对象里面的属性名和属性值内容相同,只需要写属性名即可
var utils=(function(){
var num=100;
function bannerFn(){
console.log("banner")
}
function getDate(){
console.log("date")
}
function fn(){
console.log("fn")
}
return {
initFn:function(){
bannerFn();
getDate();
},
fn,
num
}
})();
utils.initFn();
utlis.fn();
7.如果用自执行函数包裹了js文件代码,如何再实现让外界能够使用这个代码里面的函数?
- 把想要让外界访问的函数赋给window对象的一个属性,外界就能够通过属性名调用这个函数了
var num=3;
(function(){
var num=6;
function jquery(){
num++;
console.log(num);
}
window.jquery=window.$=jquery;
})();
$();
8.当用for循环给多个li添加点击事件时,如果用lis[i].onclick点击就会报错,为什么?怎么解决?
原因:for循环的初始变量var i,声明的是全局变量i,当触发点击事件时,无论是哪个lis[i],都会去全局作用域中寻找i,而这个全局变量i已经变成了3,没有lis[3]这个li,所以就会报错
如何解决:
- 方法一(推荐):可以给每个li元素设置一个自己的属性,属性值就是自己的下标,事件里的函数通过这个属性去找到数组指定下标的li
- 方法二:出现问题的根本原因是点击事件找全局变量i了,所以可以让每个点击事件后面函数用自执行函数变成私有作用域,把i传给自执行函数,变成每个li的私有变量,让每个li都形成一个私有执行上下文,再通过return要执行的函数,**实现闭包 **
<script>
var olis = document.querySelectorAll(".main li");
var odivs = document.querySelectorAll(".main>div");
for (var i = 0; i < olis.length; i++) {
olis[i].onclick = (function (i) {
return function () {
for (var j = 0; j < olis.length; j++) {
olis[j].classList.remove("current");
odivs[j].classList.remove("current");
}
olis[i].classList.add("current");
odivs[i].classList.add("current");
}
})(i)
}
</script>
- 方法三:将for循环里所有函数用自执行函数包裹,将i传入自执行函数,由于olis[i].onclick事件占用着里面的函数,所以形成了闭包
<script>
var olis = document.querySelectorAll(".main li");
var odivs = document.querySelectorAll(".main>div");
for (var i = 0; i < olis.length; i++) {
(function (i) {
olis[i].onclick = function () {
for (var j = 0; j < olis.length; j++) {
olis[j].classList.remove("current");
odivs[j].classList.remove("current");
}
olis[i].classList.add("current");
odivs[i].classList.add("current");
}
})(i);
}
</script>
- 方法四:for循环中的循环变量用let声明
- 原理:
- 在ES6中for(){}整体看做一个块儿级作用域
- var和let的变量提升区别,var会提升到当前函数作用域的最上面,let会提升到当前块儿作用域的最上面
- 如果循环变量是let创建的,那么for循环的每次执行都会形成一块新的块儿级作用域,每个作用域的i值不同
9.关于变量提升的小知识:
1.let 的「创建」过程被提升了,但是初始化没有提升。(此时变量进入暂时死区) 2.var 的「创建」和「初始化」都被提升了。 3.function 的「创建」「初始化」和「赋值」都被提升了。 4.const 和 let 只有一个区别,那就是 const 只有「创建」和「初始化」,没有「赋值」过程。(也会进入暂时死区)。
10.声明、定义、初始化和赋值的区别?
-
声明:告诉编译器或解析器该变量存在,这个行为并不分配内存空间。 -
定义:为变量分配内存空间。在C语言中,一般声明就包含定义,比如:int a;。但在Javascript中,var a;这种形式只是声明。 -
初始化:在定义变量以后,系统为变量分配的空间内存储的值是不确定的,所以需要对这个空间进行初始化,以确保程序的安全性和确定性,给变量赋默认值。 -
赋值:变量在分配空间之后的某个时间里,对变量的值进行刷新操作,即修改存储空间内的数据。
10.15
1.this的指向?
this只看调用方式,不看定义位置
块级私有作用域中没有this,只研究函数调用时产生的this
什么是回调函数:a函数作为b函数的参数,a就叫回调函数
- 1、全局、定时器、延时器里面的this:window(严格和非严格)
- 2、普通函数(前面没有.调用)、自执行函数、回调函数的this:
- 非严格模式下:this指向window
- 严格模式下:this指向undefined
- 3、绑定事件函数中的this:指向绑定当前事件的DOM元素
- 4、对象或者数组调用里面的普通函数:函数里的this指向调用它的对象或数组
- 如果用全局变量指向对象或者数组里面的函数,再用这个变量执行函数,则视为普通函数的执行,this指向window(非严格模式)
- 对象或数组里的自执行函数:函数里的this仍然是指向window(非严格模式)
- 7、箭头函数:没有自己的this,如果函数中调用了this,就沿着作用域链去找上级的this,
- **重点:**对象不会生成作用域,块级作用域没有this
- 6、new构造函数的实例:this指向实例化对象
- 7、call、apply、bind:能够指定this指向
2.私有函数生成上下文后栈中会执行什么操作?
- 1、创建私有作用域AO
- 2、初始化作用域链
- 3、初始化this指向
- 4、初始化arguments
- 5、形参赋值
- 6、变量提升
- 7、代码自上而下执行
3.堆中存放的函数,由几部分构成?
- 作用域:[[scope]] ,存放函数定义所在的父级作用域
- **代码字符串:**函数体内的代码字符串
- **键值对:**name: 存放函数的名字 、length:存放形参的个数
4.js中的设计模式你知道的有哪几种?
- 单例、工厂、构造函数、发布与订阅、观察者模式。。。
5.单例设计模式?
- 单例设计模式:两个个体独立,相互不会影响
- 高级单例模式:在个体相互独立的前提下,又想让外面能获取里面的变量。用自执行函数,形成私有作用域,return时返回对象。对象里面输入想返回的私有变量
6.工厂设计模式?
- 工厂设计模式:将常用重复语句封装到一个函数中,形成一个工厂,想使用这些语句就调用这个函数即可。
- 优点:代码一次编写,能够多次复用,实现了高内聚,低耦合
7.面向对象的三个重要概念?
- 面向对象是一种编程思想
- 面向对象的三个重要概念:对象、类、实例
- 类(构造函数):按照特征进行分类,把事物进行归纳分类,而且类一定会赋予它的每个成员,一些属性和方法
- 赋予给每个成员的属性和方法称为静态属性和方法,放在他堆中的键值对区域
- 构造函数的定义名规范为首字母大写
- 通过new调用的函数叫构造函数
- 不通过new调用的函数叫普通函数
- 实例:类中的每一个成员,都是这个类的实例,每一个实例都具有私有属性和方法,共有属性和方法
- 私有属性和方法:prototype属性外的都是私有属性和方法
- 共有属性和方法:只要变量是Array数据类型,都能调用.push()方法,.push()就是公有方法。prototype属性里的都是公有属性和方法。
- 两个实例中私有的方法做等等比较结果是false,公有的方法做等等比较结果是true
- 对象:在js中,万事万物皆对象,指代的是对象数据类型,new调用构造函数,就必须返回一个对象,所以实例的本质也是对象。
8.构造函数设计模式?
- 用new调用函数,就视为该函数是构造函数(构造函数设计模式)
- 不用new调用函数,就视为该函数是普通函数
9.构造函数执行与普通函数执行的区别?
构造函数在栈内存中执行的过程:
- 区别一:在执行六大步之前,先创建私有的实例对象
- 区别二:将构造函数的this,指向创建的实例对象
- 区别三:有this.xxx=xxx;的代码,就是给实例对象添加私有xxx属性
- 区别四:
- return无返回值或返回的是原始类型值 ----->返回实力对象
- return的是对象类型----->返回自己写的对象:所有引用类型都算是对象类型
9.js内置类?
所有原型链的终点都指向Object类
- 各种数据类型内置类:
- null和undefined没有内置类
- 基本数据类型:Number、String、Boolean、Symbol、Bigint
- 引用数据类型:Object(最大的内置类)、Array、Function、Regexp、Error、Date…
- 每一个DOM元素标签都有自己所属的类:p标签有自己的内置类、a标签有自己的内置类…
10.let f1 =new Fn与let f2=new Fn(10,20)的区别?
相同点:
- 无论有没有(),构造函数都执行了,f1和f2都实例化对象。
不同点:
10.16
1.instanceof关键字的使用?
instanceof关键字:用来判断一个实例对象,是否属于后面这个类
function Fn(){}
var fnn=new Fn();
console.log(fnn instanceof Fn);
console.log(fnn instanceof Array);
console.log(fnn instanceof Object);
2.原型和原型链?
- 几乎所有的函数都有prototype(原型)属性,
- 有prototype属性的函数类型:普通函数(实名匿名)、构造函数、生成器函数
- 没有prototype属性的函数类型:箭头函数、对象{}中用函数ES6简洁表示法编写的函数
- 每个prototype属性,都自带一个constructor属性:指向本构造函数
- 所有的对象都有
__proto__ (原型链)属性,它指向创建这个实例的类的prototype属性 - 每个prototype属性也是一个对象,所以也存在
__proto__ 属性:它指向更高一层的类的prototype属性 - Object是一切对象类型的基类
- Object的prototype属性中的
__proto__ 属性指向null,就是到头了 - prototype属性为实例提供公有方法
- 构造函数的静态方法或属性,通过函数名.方法或属性来定义,比如:Array.job=“array”;
3.原型链查找机制?
- 首先查找实例本身私有的属性和方法,如果有就用私有的
- 如果私有里面没有,就沿着
__proto__ 属性,去第一层原型链中的prototype属性中去找,如果第一层prototype上有就用第一层prototype上的公有方法 - 如果第一层prototype属性中还没有,就沿着第一层prototype属性的
__proto__ 原型链继续向上找 - 直到找到Object的prototype属性,如果它也没有就报错没有这个属性或方法。
4.如何给js内置类扩展方法?
- 以数组为例,Array内置类
- 扩展方法在构造函数的原型(prototype)上
- Array.prototype.mypush=function(){}//给Array添加方法
- 注意:函数中的this不看在哪定义,谁调用函数this就是谁。
5.构造函数的静态方法、实例的私有方法、实例的公有方法都如何调用?
- 构造函数的静态方法调用:构造函数名.静态方法
- 实例的私有方法:实例名.私有方法
- 实例的公有方法:实例名.公有方法
- 实例不能使用构造函数的静态方法。
6.链式调用?
7.in关键字和hasOwnProperty关键字?
-
in检测私有和作用域链范围,hasOwnProperty检测私有范围 -
“name” in “arr”:检测某个对象是否有某个属性或方法(不管公有私有) -
arr.hasOwnProperty(“name”):检测某个对象是否存在某个私有属性或方法 -
公有还是私有,是相对来说的,主要看针对的对象是谁
8.基本数据类型是如何调用到方法属性的?
9.getPropertypeOf()方法,手写判断是否有公有属性的方法?
10.18
1.原型重定向?
- 方式一:给构造函数的prototype属性重新指向一个新的对象,新对象中如果没有constructor,所以会沿原型链去找,如果有就用自己添加的constructor
- 方式二:给构造函数的prototype属性重新指向一个new 构造函数自己。
- 两个方式的区别:方式一的constructor,指向了Object,方式二的constructor:又指向了自己。
- 内置类的prototype属性不能被重定向,但是可以往里面增加新的方法,并且可以重写原有的方法。
2.什么情况下获取的值会是undefined?
(1)变量被声明了,但没有赋值时,就等于undefined。
(2) 调用函数时,应该提供的参数没有提供,该参数等于undefined。
(3)对象没有赋值的属性,该属性的值为undefined。
(4)函数没有返回值时,默认返回undefined。
- this.x是到对象中找属性,x是到作用域中找变量
- 变量没有被声明就会报错,被声明没定义报undefined,对象中没有的属性会返回undefined
3.undefined和null的区别?
- null表示没有值,就是这个地方不应该存在值
- undefined表示**”缺少值“**,表示这里应该有值,只是还没给值
4.函数的三种角色?
5.构造函数的__proto__ 指向哪里?
- 所有对象都是Object类的实例,Object类是所有对象的基类
- 所有函数、类,都是Function类的实例
- 构造函数即是函数,又是对象,所以构造函数中都有
__proto__ 和prototype 属性,而且__proto__ 指向的是Function类
6.Function与Object的关系?
-
Object.__proto__===Function.prototype :证明Object是Function的实例 -
Function.__proto__===Functon.prototype :为了让所有的函数都能用函数类原型上的方法 -
Function.__proto__.__proto__===Object.prototype :证明Function也是对象的实例 -
Object.__proto__.__proto__===Object.prototype :为了让所有对象能够用对象类原型上的方法 -
Function.prototype:本质上是一个匿名空函数,所有的操作都跟对象一样 -
总结:
- Object与Function互相为对方的实例
- 因为Object和Function即是对象,也是构造函数
- 当他们作为对象角色时,他们都是Object构造函数的实例,他们应该能够调用Object的方法,所以他们沿着prototype的
__proto__ (原型链)属性都会找到Object.prototype - 当他们作为函数角色时,他们都是Function构造函数的实例,他们就应该能调用Function的方法,所以他们的
__proto__ (原型链)都指向了Function.prototype - 所有的构造函数会沿着
__proto__ 属性找到Function.prototype,沿着prototype 的原型链找到Object.prototype
7.new和.运算符的优先级?
-
成员访问: (xxx.xxx) 优先级20 -
new Foo():有参数列表new 优先级20 -
new Foo :无参数列表new 优先级19 -
优先级相同时,由左到右,由内到外运算 -
new Foo.getName(): 拆分为new Foo和Foo.getName(),把Foo.getName()看成整体,找到了一个函数,再执行new +函数 -
new Foo().getName(): 拆分为new Foo()和Foo().getName(),先执行new Foo(),再执行 前面的结果.getName() -
new new Foo().getName() :由于由内到外执行,先执行第二个new以及后面的代码,分为new Foo(),Foo().getName()。优先级相同,由左到右执行。执行完new Foo(),形成了new 上一步返回值.getName() ,再进行优先级判断,分为了new 上一步返回值和上一步返回值.getName(),后者优先级高,先执行后者,获取了实例.getName()函数,再执行new 实例.getName()函数。 -
总结,重点是根据优先级得到将哪一部分看成一个整体。
|