数据类型
**null表示"没有对象",即该处不应该有值。**典型用法是:
(1) 作为函数的参数,表示该函数的参数不是对象。
(2) 作为对象原型链的终点。
Object.getPrototypeOf(Object.prototype)
// null
**undefined表示"缺少值",就是此处应该有一个值,但是还没有定义。**典型用法是:
(1)变量被声明了,但没有赋值时,就等于undefined。
(2) 调用函数时,应该提供的参数没有提供,该参数等于undefined。
(3)对象没有赋值的属性,该属性的值为undefined。
(4)函数没有返回值时,默认返回undefined。
var i;
i // undefined
function f(x){console.log(x)}
f() // undefined
var o = new Object();
o.p // undefined
var x = f();
x // undefined
个人心得:内存里面有堆和栈,变量放在栈中,引用类型的数据放在堆内存之中,
函数传值
问题:js调用函数是传递的是?
fn(a)括号里面的a会赋值给fn{}作用域里面的a相当于 var a=a
var a = 3;
function fn(a) {undefined
a = a + 1;
console.log(a);//4
}
fn(a);
console.log(a);//3
//执行到:fn(a);先读到a是3,把a的值传给形参a,形参a去加1,输出为4。实参a还是实参a,所以最后输出a仍然是3。这里是基本值传递。
function fn2(obj) {undefined
console.log(obj.name);//tom
}
var obj = {name:‘tom’};
fn2(obj);
//执行到:fn2(obj);把obj中的内容(不是把{name:‘tom’}传递给形参obj,是把obj的地址值传给形参obj,只是地址值中是 {name:‘tom’})传递给形参obj。这里是地址值传递。
var obj={}
function jjj(){
console.log(11);
console.log(this); //obj
}
jjj.call(obj)
回调函数就是你自己定义的,但是你没有自己去调用他就最终执行了(调用就是根据他的函数名字进行调用)
IIFE(立即执行函数)
(
function () {
var a = 1;
function test() {
console.log(a);
}
window.$ = function () {
return {
test:test
}
}
console.log(window.$());
}
)()
this
function Person(color){
console.log(this);
this.color=color;
this.getColor=function(){
console.log(this);
return this.color
}
this.setColor=function(color){
console.log(this);
this.color=color
}
}
// Person('red') // window
let p=new Person('blue'); //p
var obj={}
p.setColor.call(obj,"yellow") //this就是obj,
// call可以改变this指向,相当于obj.setColor
// 就是在一个对象里面调用不在对象里面的方法
p.setColor.call(obj) //obj
function fun1(){
function fun2(){
console.log(this);
}
fun2() //window
}
fun1() //window
原型链
显与隐式原型都是保存地址值
当创建对象的时候,会在里面运行这个语句
Object对象
// 用构造函数
console.log(Object); //object在这里已经存在了
console.log(Object.prototype);
function fn(name){
this.name=name,
this.test1=function(){
console.log(this.name)
}
}
fn.prototype.test2=function(){
console.log("test2");
}
let p=new fn('lqc')
console.log(fn.prototype);
p.test1()
p.test2()
console.log(fn.prototype===p.__proto__);
个人心得:不管是自己创建多少个object类型或者是function型,他们的最终都会找到他们的原型对象.例如:在创建构造函数的时候,传入的this.prototype={}这个Object字面量对象,那么他的隐式原型就会找会object的原型对象了,可以更好理解下面0*345的地址
原型链图片1:这个是实例的图片
- 函数的显示原型都是Object空实例,是在创建构造函数的时候创建的Object空实例,他们都指向Object.prototype
- var Foo=new Function()
- Function=new Function()
- 所有函数的隐式原型就是Function.prototype
- 需要注意实例与函数的原型路线
instanceof原理
//假设instanceof运算符左边是L,右边是R.L是一个实例
L instanceof R
//instanceof运算时,通过判断L的原型链上是否存在R.prototype
L.__proto__.__proto__ ..... === R.prototype ?
//如果存在返回true 否则返回false
原型链面试题
答案是:1,undfind,2,3
因为创建实例的时候,this.__ proto __=构造函数的显示原型,但是后面的显示原型地址改变了
答案:a() 未定义, a() ,b()
变量提升
fn3是一个变量名,不是函数名.只会提升变量,不会提升函数
执行上下文
全局执行上下文
函数执行上下文
a,bar,foo是全局执行上下文,遇到函数调用就压栈,等到函数执行完就出栈,全局执行只有一个,公式就是n+1
面试题1:
答案:这个需要注意,给js的传值基本数据类型传递的是值,而不是引用数据类.每个函数里面的作用域是不一样的
面试题2:
function fn(){
}
var fn;
console.log(fn);//function fn()
考察变量和函数提升谁先提升.答案是变量的优先级更高,而且变量被覆盖了
面试题3:
if (!(b in window)){
var b=1
}
console.log(b);//undefined
面试题4:(易错)
var c = 1;
function c(c) {
console.log(c);
}
c(2) //报错
ps:变量提升要看上面有没有输出语句或者函数等等,只要提升了
作用域与 作用域链
es5只有全局作用域还有函数作用域,es6多了块级作用域
面试题1:
var x = 10;
function fn() {
console.log(x);
}
function show(f) {
var x = 20;
f();
}
show(fn) //10
var f=fn是赋值地址,所以fn()的位置是不变的,不会受到f()的影响
Object创建对象的方式
方式一:
let p=new Object()
p.name='Tom'
p.age=12
p.setName=function(name){
this.name=name
}
方式二:字面量
方式三:工厂函数
function createPerson(name,age){
var obj={
name:name,
age:age,
setName:function(name){
this.name=name
}
}
return obj
}
var p=createPerson('lqc',21)
console.log(p);
定时器
如果是多线程的,两个线程中,对同一个节点p,一个线程对节点P进行删除,另一个对节点p进行文本更新,那么会导致报错!
// var start=Date.now()
// setTimeout(()=>{
// console.log(Date.now()-start);
// },1000)
// 定时器的回调函数执行要等到js主线程执行完
setTimeout(() => {
console.log("1");
// alert()
}, 1000)
setTimeout(() => {
console.log("2");
}, 2000)
setTimeout(() => {
console.log("3");
}, 0)
function fn(){
console.log('fn');
}
fn()
console.log('之前');
alert('alert') //alert会暂停主线程
console.log('之后');
// settimout的回调要到主线程执行
要知道异步执行原理,就先要了解同步执行。因为计算机程序执行分为同步执行和异步执行。
所谓的同步执行,就是正常的计算机执行的顺序流程:
1.顺序控制语句 从上至下 从左至右
2.分支控制语句 if switch
3.循环控制语句 for while do...while for...in forEach()
所谓的异步执行(回调函数),是一种特殊的程序的执行方式:
1.setInterval setTimeout
2.事件的绑定 onclick...
3.ajax请求
所谓异步程序的执行:
1,所有的异步程序的执行,都会在同步程序执行结束之后,再来执行。
2,异步程序的执行顺序,如果时间相同,看代码的先后顺序,如果时间不同,时间短的,先执行。
js分为同步任务和异步任务,异步任务分为宏任务和微任务
Object
Object.keys方法
Object.keys方法是JavaScript中用于遍历对象属性的一个方法 。它传入的参数是一个对象,返回的是一个数组,数组中包含的是该对象所有的属性名。 如:
var cat= {
name:’mini’,
age:2,
color:’yellow’,
desc:”cute”
}
console.log(Object.keys(cat)); // ["name", "age", "color", "desc"]
这里有一道关于Object.keys的题目
输出对象中值大于2的key的数组
var data = {a: 1, b: 2, c: 3, d: 4};
Object.keys(data).filter(function(x) { return 1 ;})
期待输出:[“c”,”d”] 请问1处填什么?
正确答案:1 :data[x]>2
Object.keys是es5中新增的方法,用来获取对象自身所有的可枚举的属性名,但不包括原型中的属性,然后返回一个由属性名组成的数组。注意它同for..in一样不能保证属性按对象原来的顺序输出。 Object.getOwnPropertyNames也是es5中新增的方法,返回对象的所有自身属性的属性名(包括不可枚举的属性)组成的数组,但不会获取原型链上的属性。
Array.filter(function)对数组进行过滤返回符合条件的数组。
Object.values()方法
Object.values方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历( enumerable )属性的键值。
var obj = { foo: "bar", baz: 42 };
Object.values(obj)
// ["bar", 42]
返回数组的成员顺序,属性名为数值的属性,是按照数值大小,从小到大遍历的,因此返回的顺序是b、c、a。Object.values只返回对象自身的可遍历属性。
var obj = { 100: 'a', 2: 'b', 7: 'c' };
Object.values(obj)
// ["b", "c", "a"]
如果Object.values方法的参数是一个字符串,会返回各个字符组成的一个数组。
Object.values('foo')
// ['f', 'o', 'o']
上面代码中,字符串会先转成一个类似数组的对象。字符串的每个字符,就是该对象的一个属性。因此,Object.values返回每个属性的键值,就是各个字符组成的一个数组。 如果参数不是对象,Object.values会先将其转为对象。由于数值和布尔值的包装对象,都不会为实例添加非继承的属性。所以,Object.values会返回空数组。
Object.create()
Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。 语法 Object.create(proto, [propertiesObject]) 参数 proto 新创建对象的原型对象。 propertiesObject 可选。如果没有指定为 undefined,则是要添加到新创建对象的可枚举属性(即其自身定义的属性,而不是其原型链上的枚举属性)对象的属性描述符以及相应的属性名称。这些属性对应Object.defineProperties()的第二个参数。 返回值 一个新对象,带着指定的原型对象和属性。 如:
var parent = {
x : 1,
y : 1
}
var child = Object.create(parent,{
z : { // z会成为创建对象的属性
writable:true,
configurable:true,
value: "newAdd"
}
});
console.log(child)//{z: "newAdd"}z: "newAdd"__proto__: x: 1y: 1__proto__: Object
Object.create()创建继承
function A(){
this.a = 1;
this.b = 2;
}
A.prototype.drive = function(){
console.log('drivvvvvvvvvv');
}
//方式1
function B(){}
B.prototype = Object.create(new A()); //这里采用了new 一个实例
//方式2
function C(){
A.call(this);
}
C.prototype = Object.create(A.prototype) //这里使用的是父类的原型
以上两种方式有什么区别? 1的缺点: 执行了 new,相当于运行了一遍 A ,如果在 A 里做了一些其它事情(如改变全局变量)就会有副作用。 用 A 创建的对象做原型,里面可能会有一些冗余的属性。 2模拟了 new 的执行过程
Object.hasOwnProperty()方法
判断对象自身属性中是否具有指定的属性。
obj.hasOwnProperty('name')
在某个对象是否拥有某个属性,判断的方法有很多,常用的方法就是object.hasOwnProperty('×××'),这个方法是不包括对象原型链上的方法的
var obj = {
name:'fei'
}
console.log(obj.hasOwnProperty('name'))//true
console.log(obj.hasOwnProperty('toString'))//false
以上,obj对象存在的name属性的时候,调用这个方法才是返回true,我们知道其实每个对象实例的原型链上存在toString方法,在这里打印false,说明这个方法只是表明实例对象的属性,不包括原型链上的属性。
Object.getOwnPropertyNames()方法
Object.getOwnPropertyNames()方法返回对象的所有自身属性的属性名(包括不可枚举的属性)组成的数组,但不会获取原型链上的属性。
function A(a,aa) {
this.a = a;
this.aa = aa;
this.getA = function() {
return this.a;
}
}
// 原型方法
A.prototype.aaa = function () {};
var B = new A('b', 'bb');
B.myMethodA = function() {};
// 不可枚举方法
Object.defineProperty(B, 'myMethodB', {
enumerable: false,
value: function() {}
});
Object.getOwnPropertyNames(B); // ["a", "aa", "getA", "myMethodA", "myMethodB"]
Object.getOwnPropertyNames和Object.keysq区别
Object.getOwnPropertyNames和Object.keys的区别,即Object.keys只适用于可枚举的属性,而Object.getOwnPropertyNames返回对象自动的全部属性名称。
'use strict';
(function(){
if(!Object.getOwnPropertyNames){
console.log('浏览器不支持getOwnPropertyNames');
return;
}
//人类的构造函数
var person = function(name, age, sex){
this.name = name;
this.age = age;
this.sex = sex;
this.sing = function(){
console.log('sing');
}
}
//new 一个ladygaga
var gaga = new person('ladygaga', 26, 'girl');
//给嘎嘎发放一个不可枚举的身份证
Object.defineProperty(gaga, 'id', {
value : '1234567890',
enumerable : false
});
//查看gaga的个人信息
var arr = Object.getOwnPropertyNames(gaga);
document.write(arr); //output: name,age,sex,sing,id
document.write('</br>');
//注意和getOwnPropertyNames的区别,不可枚举的id没有输出
var arr1 = Object.keys(gaga);
document.write(arr1); //output: name,age,sex,sing
})();
包装类
// 1.包装类和基本数据类型.基本数据类型遇到调用方法会包装成包装类,才可以调用方法
let num=123;
console.log(typeof num.toString());
let str="hello lqc"
console.log(str.split(" "))
console.log(str.length); //系统会自动把str变成new String(str)
// 2. 包装类转换后,立即销毁
str.name="op" //new String("op") 在这里创建并且添加数据,但是立马进行销毁
console.log(str.name); //在在上一行已经销毁了,所以找不到了,显示undefined
// 总结:Js中,我们不太需要使用包装类,系统会自动帮我们进行包装
Load 和 DOMContentLoaded 区别
Load 事件触发代表⻚⾯中的 DOM,CSS,JS,图⽚已经全部加载完毕。 DOMContentLoaded 事件触发代表初始的 HTML 被完全加载和解析,不需要等待 CSS, JS,图⽚加载。
Event Loop
1.定义: Event Loop即事件循环,是指浏览器或Node的一种解决javaScript单线程运行时不会阻塞的一种机制,也就是我们经常使用异步的原理。
2. 众所周知 JS 是阻塞单线程语言,因为在最初 JS 就是为了和浏览器交互诞生的。如果
JS 是多线程的语言的话,我们在多个线程中处理 DOM 就可能会发⽣问题(一个线程中新加
节点,另一个线程中删除节点),当然可以引用读写锁解决这个问题。
JS 在执行的过程中会产生执行环境,这些执行环境会被顺序的加入到执行栈中。如果遇到异
步的代码,会被挂起并加⼊到 Task(有多种 task) 队列中。一旦执行栈为空,Event Loop
就会从 Task 队列中拿出需要执行的代码并放入执行栈中执行,所以本质上来说 JS 中的异步
还是同步。
以上代码虽然 setTimeout 延时为 0,其实还是异步。这是因为 HTML5 标准规定这个函数
第2个参数不得少于 4 毫秒,不足会自动增加。所以 setTimeout 还是会在 script end
之后打印。
不同的任务源会被分配到不同的 Task 队列中,任务源可以分为 微任务(microtask) 和 宏任
务(macrotask)。在 ES6 规范中,microtask 称为 jobs ,macrotask 称为 task 。
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
new Promise((resolve) => {
console.log('Promise')
resolve()
}).then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
});
console.log('script end');
// script start => Promise => script end => promise1 => promise2 => setTimeout
以上代码虽然 setTimeout 写在 Promise 之前,但是因为 Promise的then回调函数是属于微任务 setTimeout 属于宏任务,所以会有以上的打印。 微任务包括 process.nextTick , promise then, Object.observe , MutationObserver 宏任务包括 script , setTimeout , setInterval , setImmediate , I/O文件读写 , UI rendering 很多有个误区,认为微任务快于宏任务,其实是错误的。因为宏任务中包括了 script, 浏览器会先执一个宏任务,接下来有异步代码的话就先执行微任务。 所以正确的一次 Event loop 顺序是这样的
- 执行同步代码,这属于宏任务
- 执行栈为空,查询是否有微任务需要执行
- 执行所有微任务
- 必要的话渲染 UI
- 然后开始下一轮 Event loop,执行宏任务中的异步代码
通过上述的 Event loop 顺序可知,如果宏任务中的异步代码有大量的计算并且需要操作 DOM 的话,为了更快的界面响应,我们可以把操作 DOM 放在微任务中。
图层
一般来说,可以把普通文档流看成一个图层。特定的属性可以⽣成⼀个新的图层。不同的图 层渲染互不影响,所以对于某些频繁需要渲染的建议单独生成一个新图层,提高性能。但也 不能生成过多的图层,会引起反作用。 通过以下几个常用属性可以生成新图层
- 3D 变换: translate3d 、 translateZ
- will-change
- video 、 iframe 标签
- 通过动画实现的 opacity 动画转换
- position: fixed