1.函数自定义的三种方式
1.函数声明式(需给函数命名,可声明提前)
function 函数名(){
}
2. 函数表达式/匿名函数:无需给函数命名,不可声明提前
var 变量名=function(){
}
3.构造函数
var 函数名=new Function()
注意:
使用new Function()创建函数对象时,其参数必须为字符串形式
var fn = new Function(a, console.log(a));
fn(1);//会报错
var fn = new Function('a', 'console.log(a)');
fn(1);//输出1
var f = function g() {
return 23;
};
console.log(typeof f);//function
console.log(typeof f());//number
console.log(typeof g);//undefined
console.log(typeof g());//Uncaught ReferenceError: g is not defined
例题重要
请问以下JS代码输出的结果是什么?
function f(x) {
console.log(x);
var x = 200;
console.log(x);
}
f(a = 100);
console.log(a);// 100 200 100
JS中的函数是非惰性求值,也就是说f(a=100)是将a=100完成计算赋值后的结果即100传入到了f函数中,传入的是值而不是逻辑,相当于f(100),同时变量a也处于函数外也即全局环境了,因此f函数里面的x一开始是传进来的100,后续被重新赋值为200.
Hello World! undefined1.先立即执行匿名函数,输出Hello World!
2.函数执行后无返回值,则输出未定义
let obj = { num1: 117 } let res = obj; obj.child = obj = { num2: 935 }; var x = y = res.child.num2; console.log(obj.child); console.log(res.num1); console.log(y);
undefined、117、935 var a=b=1 这样的代码,只声明第一个变量,不声明后面的,然后从右往左赋值,也就是 var a;b=1;a=b;
2.JavaScript的全局函数
1.编码相关:
escape()、unescape()、encodeURI()、decodeURI()、
encodeURIComponent()、decodeURIComponent()
2.数据处理:
Number()、String()
3.数字相关:
isFinite()、isNaN()、parseFloat()、parseInt()
4.特殊:
eval()
函数会将传入的字符串当做 JavaScript 代码进行执行。eval() - JavaScript | MDN例题:eval 将会返回对最后一个表达式的求值结果,最后输出10
console.log(eval('2 + 2'));
// expected output: 4
console.log(eval(new String('2 + 2')));
// expected output: 2 + 2
console.log(eval('2 + 2') === eval('4'));
// expected output: true
console.log(eval('2 + 2') === eval(new String('2 + 2')));
// expected output: false
eval(`{
1 + 3;
a = 2;
x:
break
x;
3;
4 + 6;
;
}`)
js里有域的概念,函数外边的是全局值,在函数里是局部域,可以读取外部变量,但是这个局部域里又重新定义,就不会再读取外部,覆盖
在函数里面没有用var定义的变量是全局变量
3.立即执行函数
例1
立即执行函数没加()调用,相当于只是输入函数名,返回整个函数体
(
function
(){
console.log(1);
})
例2
var a = 10; //函数已经在体内找到名为a的了 //因此不会到全局作用域去找全局变量a(var a = 10) (function a() { a = 20; //这里的a是函数名,相当于修改函数a=20,无效 console.log(a); //a是函数名,打印整个a函数体 })()
以上代码打印:
注意:变量名和函数名相同
例3
执行以下程序,输出结果为()
let num = (
function
(x){
delete
x;
return
x;
})(1);
console.log(num);
变量num用于接收立即执行函数的返回结果
在立即执行函数内部,由于函数的形参具有DontDelete特性,所以无法使用delete操作符对其进行删除,也就是delete x是无效的。
因此,在立即执行函数外,实参1将传递给形参x,然后形参x作为函数的执行结果返回,故num值为1
关于delete delete 操作符 - JavaScript | MDN
- 任何使用 var 声明的属性不能从全局作用域或函数的作用域中删除。
- 这样的话,delete操作不能删除任何在全局作用域中的函数(无论这个函数是来自于函数声明或函数表达式)
- 除了在全局作用域中的函数不能被删除,在对象(object)中的函数是能够用delete操作删除的。
- 任何用let或const声明的属性不能够从它被声明的作用域中删除。
- 不可设置的(Non-configurable)属性不能被移除。这意味着像Math, Array, Object内置对象的属性以及使用Object.defineProperty()方法设置为不可设置的属性不能被删除
- 如果试图删除的属性不存在,delete不起作用,但仍会返回true
- 如果对象的原型链上有一个与待删除的属性同名的属性,那么删除属性之后,对象会使用原型链上的那个属性(也就是说delete属性只会在自身的属性上起作用)
// 在全局作用域创建 adminName 属性
adminName = 'xyz';
// 在全局作用域创建 empCount 属性
// 因为我们使用了 var,它会标记为不可配置。同样 let 或 const 也是不可配置的。
var empCount = 43;
EmployeeDetails = {
name: 'xyz',
age: 5,
designation: 'Developer'
};
// adminName 是全局作用域的一个属性。
// 因为它不是用 var 创建的,所在可以删除。
// 因此,它是可配置的。
delete adminName; // 返回 true
// 相反,empCount 是不可配置的,
// 因为创建它时使用了 var。
delete empCount; // 返回 false
// delete 可用于删除对象的属性
delete EmployeeDetails.name; // 返回 true
// 甚至属性不存在,它也会返回 "true"
delete EmployeeDetails.salary; // 返回 true
// delete 对内建静态属性不起作用
delete Math.PI; // 返回 false
// EmployeeDetails 是全局作用域的一个属性。
// 因为定义它的时候没有使用 "var",它被标记为可配置。
delete EmployeeDetails; // 返回 true
function f() {
var z = 44;
// delete 对局部变量名不起作用
delete z; // 返回 false
}
function Foo() {
this.bar = 10;
}
Foo.prototype.bar = 42;
var foo = new Foo();
// 返回 true,因为删除的是 foo 对象的自身属性
delete foo.bar;
// foo.bar 仍然可用,因为它在原型链上可用。
console.log(foo.bar); //42
// 从原型上删除属性
delete Foo.prototype.bar; //true
// 由于已删除“ bar”属性,因此不能再从Foo继承它。
console.log(foo.bar); //undefined
例题:
执行完如下程序后,所有能被访问到的变量包括()
在eval中使用var声明的全局变量可以被delete删除,所以变量c能删除成功; 使用var声明的全局变量或者局部变量一般是不能被delete删除的,所以变量a无法被删除,仍然可以访问到,而 未使用var声明的全局变量可以使用delete进行删除,所以无法访问到b。综上,只有变量a未被成功删除,可以访问得到,故正确答案为C选项。
1
2
3
4
5
6
var
a = 1;
b = 2;
eval(
'var c = 3'
);
delete
a;
delete
b;
delete
c;
一个函数例题:
1.参数的默认值
函数的参数后面用等号(=),可以为参数赋一个默认值
执行f(2),2作为函数的参数,忽略参数的默认值(即使括号中的 x = x 有错误),输出2
2.暂时性死区 ES6 入门教程
执行f(),因为函数的参数变量是默认声明的,括号中的 x = x 相当于 let x = x,导致错误:Cannot access 'x' before initialization,初始化之前无法访问'x'(注意,错误信息不是 x is not defined)
关于class
依据以下JS代码,在位置A打印变量a与在位置B打印变量a各会有怎样的输出?
var
a = 1;
function
test(){
// 位置A
class a {}
// 位置B
}
test();
答案:报错、class a {}
解释:
class的声明特征跟const和let类似,都是作用于块级作用域,预处理阶段则会屏蔽外部变量。因此在声明之前访问变量a都会报错,在声明之后访问才可以正常输出。
执行以下程序,输出结果为()
class Phone{
constructor(price){
this.price = price;
}
get price(){
return 999;
}
}
var p = new Phone(888);
console.log(p.price);// 报错
当类中一个属性只有get()方法而无set()方法时,该属性是无法进行赋值的,连构造方法中的初始化都不行,因此,当对象的price属性在构造方法中进行初始化,会抛出异常
4. document对象方法
如何获取元素
1.根据ID获取 getElementById(), 注意ID是唯一的
2. 根据标签名获取 getElementsByTagName()
注意:返回的是获取过来得对象的集合,以伪数组的形式存储的
还可以获取某个元素(父元素)内部所有指定标签名的子元素
element.getElementsByTagName('标签名’)
3.通过HTML5新增的方法获取
document.getElementsByClassName('类名’)//根据类名返回元素的对象集合
document.querySelector('选择器’) //根据指定选择器返回第一个元素,注意需加对应的符号
4.获取特殊元素(body,html)
document.body //返回body元素
document.ducumentElement //返回html元素
例题 1
例2
以下代码中,给class为test的div对象设置红色背景的正确js代码为( B )
<div class="test"></div>
A.document.getElementsByClassName("test").style.backgroundColor="red";
B.document.getElementsByClassName("test")[0].style.backgroundColor="red";
C.document.getElementsByClassName("test")[0].style.background-color="red";
D.document.getElementsByClassName("test").style.background-color="red";
考点:
1. document.getElementsByClassName()返回一个数组,由于该页面仅有一个class=test的元素,所以通过[0]选择到该元素
除了ID选择器的唯一性和querySelector返回单个节点,其余返回节点列表
2. 通过js选择css属性时,属性都为驼峰的形式,所以为style.backgroundColor
例3
el是一个id="id1"的div元素,以下哪行代码会执行失败 B
A. el.className='aaa'
B.el.tagName='p'
C.el.innerHTML=''
D.el.id='id2'
考点:
className id innerHTML都是可读可写的
tagName只可读不可写,即:y
className 属性设置或返回元素的 class 属性
tagName 属性返回元素的标签名
innerHTML 属性设置或返回表格行的开始和结束标签之间的 HTML
id 属性设置或者返回元素的 id
例题 4
<form name="a">
<select name="a" size="1" id="obj">
<option value="a">1</option>
<option value="b">2</option>
<option value="c">3</option>
</select>
</form>
<script>
window.onload = function() {
//首先获得下拉框的节点对象;
var obj = document.getElementById("obj");
//1.如何获得当前选中的值?:
var value = obj.value;
console.log(value);//a
//2.如何获得该下拉框所有的option的节点对象
var options = obj.options;
console.log(options);//一个数组对象
//注意:得到的options是一个对象数组
//3.如何获得第几个option的value值?比如我要获取第一option的value,可以这样:
var value1 = options[0].value;
console.log(value1);//a
//4.如何获得第几个option的文本内容?比如我要获取第一option的文本,可以这样:
var text1 = options[0].text;
console.log(text1);//1
//5.如何获得当前选中的option的索引?
var index = obj.selectedIndex;
console.log(index);//0
//6.如何获得当前选中的option的文本内容?
//从第2个问题,我们已经获得所有的option的对象数组options了
//又从第5个问题,我们获取到了当前选中的option的索引值
//所以我们只要同options[index]下标的方法得到当前选中的option了
var selectedText = options[index].text;
console.log(selectedText);//1
}
</script>
例题 5
<button>点击</button>
<script>
function a() {
console.log(1);
return function b() {
console.log(2);
}
}
var btn = document.querySelector('button');
btn.onclick = a();//1
btn.onclick = a;//2
</script>
解释
注释(掉)①
即执行btn.onclick = a ,相当于
btn.onclick = function () {
console.log(1)
return function b() {
console.log(2)
}
}
//注释掉1时,执行时不会有输出,只有点击按钮才会执行,输出1,后面的return在此时没有意义不会执行,选B
执行结束后不会有输出:相当于把函数a赋值给btn的点击事件处理程序(此时函数a不会执行)。点击按钮后打印1,返回值中的函数b不会执行注释(掉)②
即执行btn.onclick = a(),相当于
btn.onclick = function () {
console.log(2)
}
//注释掉2时,代码执行流程是:执行a()函数,输出1,点击后执行b,输出2
例题
以下哪些事件会在页面加载完成(onload)之前触发? A D
A. readystatechange
B. pageshow
C. beforeunload
D. DOMContentLoaded
解释:
readystatechange 读取状态变化 pageshow 页面展示 beforeunload 页面退出 DOMContentLoaded dom内容加载完成
改变元素内容
1.innerHTML:从对象的起始位置到终止位置的全部内容,包括Html标签,保留空格和换行
2.innerText: 从起始位置到终止位置的内容, 但它去除Html标签,同时空格和内容也会去掉
3..outerHTML:除了包含innerHTML的全部内容外, 还包含对象标签本身
5.DOM的重绘与重排
1.什么是reflow和repaint(原文链接:浏览器渲染页面原理 - 331415706 - 博客园)
reflow:例如某个子元素样式发生改变,直接影响到了其父元素以及往上追溯很多祖先元素(包括兄弟元素),这个时候浏览器要重新去渲染这个子元素相关联的所有元素的过程称为回流
repaint:如果只是改变某个元素的背景色、文字颜色、边框颜色等等不影响它周围或内部布局的属性,将只会引起浏览器 repaint(重绘)。repaint 的速度明显快于 reflow
简要:整个在浏览器的渲染过程中(页面初始化,用户行为改变界面样式,动画改变界面样式等)reflow(回流)和repaint(重绘) 会大大影响web性能,尤其是手机页面。因此我们在页面设计的时候要尽量减少reflow和repaint
2.什么会引起重绘与重排
reflow:几乎是无法避免的。现在界面上流行的一些效果,比如树状目录的折叠、展开(实质上是元素的显示与隐藏)等,都将引起浏览器的 reflow。鼠标滑过、点击……只要这些行为引起了页面上某些元素的占位面积、定位方式、边距等属性的变化,都会引起它内部、周围甚至整个页面的重新渲染。通常我们都无法预估浏览器到底会 reflow 哪一部分的代码,它们都彼此相互影响着。
下面情况会导致reflow发生
1:改变窗口大小
2:改变文字大小
3:内容的改变,如用户在输入框中敲字
4:激活伪类,如:hover
5:操作class属性
6:脚本操作DOM
7:计算offsetWidth和offsetHeight
8:设置style属性
6.浏览器的内置对象管理模型,简称BOM(Browser Object Model)中的Histroy属性和方法
1.length 返回浏览器历史列表中的URL数量
2.back() 加载 history列表中的前一个URL。
3.forward() 加载 history 列表中的下一个URL。
4.go() 加载history列表中的某个具体页面。
虚拟 DOM 最大的优势在于抽象了原本的渲染过程,实现了跨平台的能力,而不仅仅局限于浏览器的 DOM,可以是安卓和 IOS 的原生组件,可以是近期很火热的小程序,也可以是各种 GUI
7.Cookie、sessionStorage、localStorage的区别
相同点:
存储在客户端
localStorage只在客户端存储,不参与服务器通信
cookie可以在浏览器端生成
localStorage 里面存储的数据没有过期时间设置,而存储在 sessionStorage 里面的数据在页面会话结束时会被清除。
8. localStorage相关操作
1、保存数据到本地
第一个参数是保存的变量名,第二个是赋给变量的值
localStorage.setItem('Author', 'local');
2、从本地存储获取数据
localStorage.getItem('Author');
3、从本地存储删除某个已保存的数据
localStorage.removeItem('Author');
4、清除所有保存的数据
localStorage.clear()
9. 原型 原型链
主要有以下三点:
1)函数(或构造函数)身上才有 prototype (prototype名字叫原型,原型是一个对象);
2)而其他任何通过构造函数实例化出来的对象(不包括null、undefined)身上都有 __proto__(__proto__是隐式原型,隐式原型也一个对象)3)实例化对象的__proto__ 就是 构造函数的 prototype (===关系)
附:undefind 和 null 既没有prototype也没有 __proto__ ,因为它俩不是函数,也不是函数实例化出来的对象
1、所有的引用类型(数组、对象、函数),都具有对象特性,即可自由扩展属性;
2、所有的引用类型(数组、对象、函数),都有一个__proto__(隐式原型)属性,属性值是一个普通的对象;
3、所有的函数,都有一个prototype(显式原型)属性,属性值也是一个普通对象;
4、所有的引用类型(数组、对象、函数),__proto__属性值指向(完全相等)它的构造函数的“prototype”属性值;
5、当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去__proto__(即它的构造函数的prototype中)寻找
例题:
var F=function(){};
Object.prototype.a=function(){};
Function.prototype .b=function(){};
var f=new F();关于这段代码的描述,正确的是:A
A.f能取到a,但取不到b B.f能取到a,b C.F能取到b,不能取到a D.F能取到a,不能取到bconsole.log(f instanceof Object); //true
console.log(f instanceof Function); //false
//所以 用new操作符创建的f只是对象,所以只能继承于Object.prototype
console.log(F instanceof Object); //true
console.log(F instanceof Function); //true
console.log(F.a); //ƒ () {}
console.log(F.b); //ƒ () {}
console.log(f.a); //ƒ () {}
console.log(f.b); //undefined
涉及到原型继承:
1)f.__proto__ === f[的构造函数].prototype === F.prototype
2)F.prototype.__proto__ === (F.prototype)[的构造函数].prototype === Object.prototype (所以a能够 通过f.a访问)
3)f.constructor === F
4)F.__proto__ === F[的构造函数].prototype === Function.prototype (所以b可以通过 f.constructor.b访问到)
console.log(f.__proto__ === F.prototype); //true
console.log(F.prototype.__proto__ === Object.prototype); //true
console.log(f.constructor === F);//true
console.log(F.__proto__ === Function.prototype);//true
console.log(f.constructor.b);//ƒ () {}
类似美团面试例题:
Function.prototype.a = 'a'; Object.prototype.b = 'b'; function Person(){}; var p = new Person(); console.log('p.a: '+ p.a); // p.a: undefined console.log('p.b: '+ p.b); // p.b: b 解释: 1)Person函数才是Function对象的一个实例,所以通过Person.a可以访问到Function 原型里面的属性, 如果打印 console.log('Person.a: ' + Person.a),输出Person.a: a 2)new Person()返回来的是一个对象,它是Object的一个实例,是没有继承Function的,所以无法访问Function原型里面的属性 3)由于在js里面所有对象都是Object的实例,所以,Person函数可以访问到Object原型里面的 属性,Person.b => 'b'