js基础知识

164 阅读5分钟

1.箭头函数

a.当箭头函数的函数体只有一个return语句时可以省略return关键字和方法体的花括号

b.如果要写花括号则必须写return关键字,两者只写其一就会出错

c. var arr = [{id:3,type:'cash'},{id:4,type:'goods'}]; arr.filter((item)=> item.id==4) 得到一个长度为1的数组,如果arr.filter((item)=> {item.id==4})将得到一个空数组

d.箭头函数里没有this,会往外一层去找this

2.连续赋值

var a = { n: 1 };
var b = a;
a.x = b = { n: 2 };
console.log(a.x);// { n: 2 }
console.log(a);// {x: {n:2}, n: 1}
console.log(b.x);// undefined

3.左移右移

>>> 无符号右移 作用:取整向下
a >> b 有符号右移  作用:a向右移动b位,左边添0补齐;当a是小数时,仍然是向下取整
a << b 有符号左移  作用:a向左移动b位,右边添0补齐;

4.javascript:;的作用及使用场景

场景1:a标签中

<a href="javascript:;"></a>

作用:防止点击a标签时跳转到其他页面,实质上这样做就是为了添加空链接(因为js代码中什么也不执行);和javascript:void(0);有相同的功能.

5.!的作用

a.取反

b.转换为布尔类型

c.调用函数

function() {} () //这种方式会出错
!function() {} () //这样能正常调用该方法

!!强制转换为布尔类型,也相当于!(!a)取两次反

6.  ?. 和??的使用方法

?.的使用方法

obj?.name  ===>  obj对象存在时取obj.data的值,当obj对象不存在时不会报错,返回undefined

??的使用方法

空值合并操作符(??)是一个逻辑操作符,当左侧的操作数为 null 或者 undefined 时,返回其右侧操作数,否则返回左侧操作数。

??相当于赋默认值,不过它是忽略0,""等错误值的 0 ?? 'xx'===>0   ""??'xx'===>""   1 ?? 'xx'===>1     null ??'xx' ===> 'xx'

?.和??搭配使用更好,比如:

obj?.data?.name ?? 'Jay' ===>obj && obj.data && obj.data.name || 'Jay'

7. 数字转为字符串

  1. 双点解析 1223..toString() ===> '1223'
  2. 括号   (1223).toString() ===> '1223
  3. 加空串   1223 + '' ===> '1223'

8.forEach和map的区别

map会创建一个新的数组,其中每一个元素由调用数组中的每一个元素执行提供的函数得来.

map()会分配内存空间存储新数组并返回,forEach()不会返回数据。

forEach()允许callback更改原始数组的元素。map()返回新的数组。

9.在JavaScript中,有三种常用的绑定事件的方法:

 1. 在DOM元素中直接绑定;

 2. 在JavaScript代码中绑定;`elementObject.onXXX=function(){// 事件处理代码}` 当绑定多个方法时只有最后一个会被执行,前面的会被覆盖

 3. 绑定事件监听函数

10.兼容各种浏览器给事件添加事件处理对象

var addEvent = function(element,type,handler){ 
            if(element.addEventListener){  //DOM2级
                element.addEventListener(type,handler,false); //false为冒泡,true为捕获
            }else if(element.attachEvent){  //IE
                element.attachEvent("on"+type,function(){  //默认皆为冒泡
                    handler.call(element);
                });
            }else{ 
                element["on"+type] = handler; //DOM0级
            }
        };

11. addEventListener 与attachEvent的对比

相同点:

  • 使用attachEvent和addEventListener时可以实现多个事件处理函数的调用(可以绑定多个s事件处理函数,并且这些函数在对应事件被触发时都能被执行)

不同点:

  1. attachEvent是IE有的方法,它不遵循W3C标准,而其他的主流浏览器如FF等遵循W3C标准的浏览器都使用addEventListener,所以实际开发中需分开处理。
  2. 2.多次绑定后执行的顺序是不一样的,attachEvent是后绑定先执行,addEventListener是先绑定先执行(useCapture为false),即addEventListener执行顺序可控,而attachEvent不可控。
  3. attachEvent仅需要传递两个参数,而addEventListener需要传递三个参数,这里牵扯到“事件流”的概念。侦听器在侦听时有三个阶段:捕获阶段、目标阶段和冒泡阶段。顺序为:捕获阶段(根节点到子节点检查是否调用了监听函数)→目标阶段(目标本身)→冒泡阶段(目标本身到根节点)。此处的参数确定侦听器是运行于捕获阶段、目标阶段还是冒泡阶段。 如果将 useCapture 设置为 true,则侦听器只在捕获阶段处理事件,而不在目标或冒泡阶段处理事件。 如果useCapture 为 false,则侦听器只在目标或冒泡阶段处理事件。 要在所有三个阶段都侦听事件,请调用两次 addEventListener,一次将 useCapture 设置为 true,第二次再将useCapture 设置为 false。
  4. 绑定时,attachEvent必须带on,如onclick,onmouseover等,而addEventListener不能带on,如click,mouseover。这个区别在以上代码中可见。
  5. 解绑事件时:addEventListener ==> removeEventListener,  attachEvent ==>detachEvent
  6. 兼容性:attachEvent兼容:IE7、IE8;不兼容firefox、chrome、IE9、IE10、IE11、safari、opera; 而addEventListener兼容:firefox、chrome、IE、safari、opera;不兼容IE7、IE8。

12.通过JS代码设置HTML元素及样式

let str = `<div style=\"text-align: center;width:150px;height: 50px;\"><p style=\"font-size: 12px;color: #999;margin: 0;\" id=\"title\">${_this.x1}</p><p style=\"font-size: 18px;color: #343434;margin: 0;font-weight: bold;\" id=\"money\"></p></div>`

这样str字符串就可以显示一个div元素,并且也设置了样式

13.获取dom元素

let dom = document.getElementById('idname');//不要加#,jQuery里面才加
let dom = document.getElementByClassName('classname');//不要加.,jQuery里面才加

14.javascript是词法作用域

因为 JavaScript 采用的是词法作用域,函数的作用域在函数定义的时候就决定了。JavaScript 函数的执行用到了作用域链,这个作用域链是在函数定义的时候创建的。

var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f();
}
checkscope();

执行结果: local scope

var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f;
}
checkscope()();

执行结果: local scope

原因:函数作用域是在定义时确定的,不是调用时确定的.当然,这里根据闭包分析也能得到同样的结果.

15.执行上下文

JavaScript 的可执行代码(executable code)的类型有三种: 全局代码、函数代码、eval代码。当执行到一个函数的时候,就会进行准备工作,这里的“准备工作”,让我们用个更专业一点的说法,就叫做"执行上下文(execution context)"。即遇到这三种类型的代码就会创建一个执行上下文。

如何管理创建的那么多执行上下文呢?所以 JavaScript 引擎创建了执行上下文栈(Execution context stack,ECS)来管理执行上下文。执行一个函数事入栈,执行完毕出栈.

全局上下文中的变量对象就是全局对象呐!活动对象是在进入函数上下文时刻被创建的,它通过函数的 arguments 属性初始化(arguments 属性值是 Arguments 对象)。

执行上下文的代码会分成两个阶段进行处理:分析和执行,(我们也可以叫做:进入执行上下文和代码执行),分析时会进行argument对象初始化,var 变量提升,以及function提升. 执行时会对变量进行赋值.

注意: 函数中未声明一个变量,直接对其赋值,那么这个变量将挂载到全局对象上.(a=2;)

console.log(foo);
function foo(){
    console.log("foo");
}
var foo = 1;
//打印的是函数 ƒ foo(){console.log("foo");}

原因:在进入执行上下文时,首先会处理函数声明,其次会处理变量声明,如果变量名称跟已经声明的形式参数或函数相同,则变量声明不会干扰已经存在的这类属性。其实就是,变量提升的优先级低于函数提升.

console.log(foo);
function foo() {console.log('foo1')};
function foo(){
    console.log("foo");
}
// 执行结果  ƒ foo(){console.log("foo");}

原因: 当函数声明跟已经声明的函数相同,则会覆盖之前声明的函数.

16.参数按值传递

ECMAScript中所有函数的参数都是按值传递的。

其实,参数在传递时有三种方式:

  1. 按值传递
  2. 引用传递
  3. 共享传递

注意: 按引用传递是传递对象的引用,而按共享传递是传递对象的引用的副本!

最后,你可以这样理解:参数如果是基本类型是按值传递,如果是引用类型按共享传递。但是因为拷贝副本也是一种值的拷贝,所以在高程中也直接认为是按值传递了。

var obj = {
    value: 1
};
function foo(o) {
    o.value = 2;
    console.log(o.value); //2
}
foo(obj);
console.log(obj.value) // 2
/*
*这个例子看似是引用传递,但其实是共享传递.
*/

接着看下面的例子:

var obj = {
    value: 1
};
function foo(o) {//参数o是指向obj对象的引用的副本
    o = 2;//将这个引用的值设置为2,相当于断了与obj的连接,即o不再指向obj,所以后面再怎么修改都不会对obj产生任何影响
    console.log(o); //2
}
foo(obj);
console.log(obj.value) // 1
/*
*共享传递是指,在传递对象的时候,传递对象的引用的副本。所以修改 o.value,可以通过引用找到原值,但是直接修改 o,并不会修改原值。所以以上两个例子其实都是按共享传递。
*/

例子接着看:

var obj = {
    value: 1
};
function foo(o) {
    o.x = 1;
    console.log(o);  //{value: 1, x: 1}
    o = 2;这里的修改将不会对obj生效,因为断了连接
    o.y = 3;//o此时是一个基本类型,这样赋值不会生效(也不会报错)
    console.log(o);// 2
}
foo(obj);
console.log(obj) //{value: 1, x: 1}

又一个例子:

var obj = {
    value: 1
};
function foo(o) {
    o.x = 1;
    console.log(o); // {value: 1, x: 1}
    o = {};//断连接
    o.y = 3;
    console.log(o);//{y: 3}
}
foo(obj);
console.log(obj){value: 1, x: 1}

总结:javascript中参数的传递是按值传递,只是当参数是引用类型时是共享传递,传递的是引用的副本.

17.Map和Object的区别

Map

**Map对象保存键值对。任何值(对象或者原始值) 都可以作为一个键或一个值。构造函数Map可以接受一个数组作为参数。
**

Map和Object的区别

  • 一个Object 的键只能是字符串或者 Symbols,但一个Map 的键可以是任意值。
  • Map中的键值是有序的(FIFO 原则),而添加到对象中的键则不是。
  • Map的键值对个数可以从 size 属性获取,而 Object 的键值对个数只能手动计算。
  • Object 都有自己的原型,原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。

Map对象的属性

  • size:返回Map对象中所包含的键值对个数

Map对象的方法

  • set(key, val): 向Map中添加新元素
  • get(key): 通过键值查找特定的数值并返回
  • has(key): 判断Map对象中是否有Key所对应的值,有返回true,否则返回false
  • delete(key): 通过键值从Map中移除对应的数据
  • clear(): 将这个Map中的所有元素删除

遍历方法

  • keys():返回键名的遍历器
  • values():返回键值的遍历器
  • entries():返回键值对的遍历器
  • forEach():使用回调函数遍历每个成员

Map与其他数据结构的相互转换

  • map与数组相互转换

    const arr = [[{'a': 1}, 111], ['b': 222]]

    const myMap = new Map(arr)

    [...myMap] // map转数组。 [[{'a': 1}, 111], ['b': 222]]

  • map与对象相互转换

    const obj = {} const map = new Map(['a', 111], ['b', 222]) for(let [key,value] of map) { obj[key] = value } console.log(obj) // {a:111, b: 222}

  • map与JSON相互转换

**JSON字符串要转换成Map可以先利用JSON.parse()转换成数组或者对象,然后再转换即可。
**

18.Map和Set的区别

set是一种关联式容器,其特性如下:

  • set以红黑树(RBTree)作为底层容器
  • 所得元素的只有key没有value,value就是key
  • 不允许出现键值重复
  • 所有的元素都会被自动排序
  • 不能通过迭代器来改变set的值,因为set的值就是键

map和set一样是关联式容器,它们的底层容器都是红黑树,区别就在于map的值不作为键,键和值是分开的。它的特性如下:

  • map以RBTree作为底层容器
  • 所有元素都是键+值存在
  • 不允许键重复
  • 所有元素是通过键进行自动排序的
  • map的键是不能修改的,但是其键对应的值是可以修改的

Set

Set对象允许你存储任何类型的值,无论是原始值或者是对象引用。它类似于数组,但是成员的值都是唯一的,没有重复的值。

Set 本身是一个构造函数,用来生成Set 数据结构。Set函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化。

Set中的特殊值

Set 对象存储的值总是唯一的,所以需要判断两个值是否恒等。有几个特殊值需要特殊对待:

  • +0 与 -0 在存储判断唯一性的时候是恒等的,所以不重复
  • undefined 与 undefined 是恒等的,所以不重复
  • NaN 与 NaN 是不恒等的,但是在 Set 中认为NaN与NaN相等,所有只能存在一个,不重复。

Set实例对象的属性

  • size:返回Set实例的成员总数。

Set实例对象的方法

  • add(value):添加某个值,返回 Set 结构本身(可以链式调用)。

  • delete(value):删除某个值,删除成功返回true,否则返回false

  • has(value):返回一个布尔值,表示该值是否为Set的成员。

  • clear():清除所有成员,没有返回值。

遍历方法

  • keys():返回键名的遍历器。
  • values():返回键值的遍历器。
  • entries():返回键值对的遍历器。
  • forEach():使用回调函数遍历每个成员。

由于Set结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys方法和values方法的行为完全一致。

set对象的作用

  • 数组去重(利用扩展运算符)

    const mySet = new Set([1, 2, 3, 4, 4]) [...mySet] // [1, 2, 3, 4]

  • 合并两个set对象

    let a = new Set([1, 2, 3]) let b = new Set([4, 3, 2]) let union = new Set([...a, ...b]) // {1, 2, 3, 4}

  • 交集

    let a = new Set([1, 2, 3]) let b = new Set([4, 3, 2]) let intersect = new Set([...a].filter(x => b.has(x))) // {2, 3} 利用数组的filter方法

  • 差集

    let a = new Set([1, 2, 3]) let b = new Set([4, 3, 2]) let difference = new Set([...a].filter(x => !b.has(x))) // {1}

19.数组的every, some, map,forEach, filter方法的作用

every

不期望它能对原始数组进行修改。它的定位是考察数组地整体特性,也就是考察数组中所有元素的共性。它关注的是数组整体元素的共性。只要有一个不满足,循环就会结束,接下来的数据就不会继续判断。注意: every方法有返回值,返回值是一个布尔值。只有所有元素都具备某项特性后,才会返回true。只要有一项不满足就返回false。

var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];
 
let bool = numbers.every(function(item, index, list) {
  return item % 2 == 0;
});
console.log(bool);

some

它的定位是考察数组的个性。比如考察数组中是否存在一个等于0的数。它关注的是数组的个性。只要有一个满足,循环就会结束,接下来的数据就不会继续判断。

let bool = numbers.some(function(item, index, list) {
  return item == 4;
});
console.log(bool);

filter

它致力于从已有的数组中筛选出符合一定条件的数据项,最后的返回值是所有符合条件的数据项构成的数组。它不会修改原来的数组。记住,它的立足点就是筛选。也仅仅是筛选。还有一点需要注意:每一次遍历都会有一个返回值,它的类型是布尔类型。返回值只有是true,当前遍历项才会被筛选中。不要试图在filter中去修改原始数组。 

let cities = [beijing, xian, hubei, hunan, sichuan];
let topArr = cities.filter(function(item, index, list) {
  return item.level != 1;
});
console.log(topArr);

map

map的本意就是映射,也就是将一个值从一种形式映射到另一种形式,比如将key映射到value。它的每一次遍历都会有一个返回值。这些返回值组合成最终的结果数组。

var numbers = [1, 2, 3, 4, 5, 6];
var capitals = ["北京都", "南京都", "广州都", "重庆都", "西安都", "拉萨都"];
let targets = numbers.map(function(item, index, list) {
  return capitals[item-1];
});
console.log(targets);

forEach

它或许是最为常用的方法,它至于遍历,可以在获取当前数据项的前提下,对数据进行修改。它没有返回值。会修改原来的数组。

let capitals = ["北京都", "南京都", "广州都", "重庆都", "西安都", "拉萨都"];
capitals.forEach(function(capital) {
  console.log(capital);
});

除了forEach以外, 其他四种方法每次遍历的时候都会有返回值。

20.数组扁平化

1.Array.flat()方法

a.flat() 函数不会以任何方式改变原始数组中的任何普通或嵌套数组,因此在使用该函数之前无需维护这些数组的状态。flat() 函数唯一会改变的数组是函数完成后返回的全新数组,它只是使用原始数组的所有内容构建的。

b.flat() 函数将删除原始数组中存在的所有空值。

flat() 函数仅采用一个参数,该参数是可选的,唯一的参数是 depth 参数。如果原始数组包含一个或多个嵌套数组结构,则此参数决定函数将多少数组层压扁为单个层。由于该参数是可选的,所以它的默认值为 1,并且在函数完成时,只有单层数组将被平展到返回的全新数组中。

depth为正整数

若depth参数为2,那么在原始数组中深度最大为2的任何数组都将被完全展平,以便将其所有内容单独连接到新数组。原始数组中深度为3或更大的任何数组的深度将减少2,并且这些数组中深度为1或2的任何单个数组项将单独连接到新数组。

****depth参数为Infinity

当参数为Infinity时,原始数组中具有任何深度的所有数组都将被展平,以便将其所有内容单独连接到新数组。

depth参数为 0

使用深度参数值 0 调用 flat() 函数。这意味着原始数组中包含的任何数组都不会被展平,并且新数组的单个数组项和嵌套数组的组成与原始数组完全相同。

depth参数为负数

使用 depth 参数值 -Infinity 调用 flat() 函数。由于负深度值对于扁平嵌套数组没有意义,所以在指定负深度参数值的情况下,将使用 0 作为替代。正如前面的示例所演示的那样,当指定深度参数值为 0 时,原始数组中没有数组是扁平的,而新数组中各个数组项和嵌套数组的组成与原始数组完全相同。

2.数组扁平化的方法

  • arr.flat(Infinity)

  • JSON.stringify结合字符串替换

    let str = JSON.stringify(ary); str.replace(/[[]]/g, '').split(',')

  • 递归处理

    let result = [];let fn = function(ary) { for(let i = 0; i < ary.length; i++) { let item = ary[i]; if (Array.isArray(ary[i])){ fn(item); } else { result.push(item); } } }

  • 扩展运算符

    while (ary.some(Array.isArray)) { ary = [].concat(...ary); }

21.ES6新特性

文章1: www.alloyteam.com/2016/03/es6…

  1. let, const(暂时性死区(TDZ))

  2. 箭头函数

    箭头函数的特性:
    1、箭头函数是匿名函数,不绑定自己的this,arguments,super,new.target2、箭头函数会捕获其所在上下文的this值,作为自己的this值,在使用call/apply绑定时,相当于只是传入了参数,对this没有影响3、箭头函数不绑定arguments,取而代之用rest参数…解决4、箭头函数当方法使用的时候,没有定义this绑定5、箭头函数不能作为构造函数,和 new 一起用就会抛出错误6、箭头函数没有原型属性7、不能简单返回对象字面量
    
  3. 模板字符串 'my name is ${name}'

    使用模板字符串的好处:
    a. 在ES5拼接的时候,如果结构需要换行,必须使用转义符;在ES6中,使用模板字符串,直接使用html结构即可b. 在ES5中,单引号和双引号必须注意嵌套问题,在ES6中,就不需要考虑;c. 模板字符串中,可以写js语句d. 方便简洁,不容易出错;
    
  4. Set和Map

  5. Promise

  6. class类

  7. Symbol 

    ES6新增了Symbol数据类型,它用来生成一个独一无二的值,
    它Symbol数据常用来给对象属性赋值,让对象属性具备唯一性,不容易被覆盖。
    
  8. 解构赋值 (对赋值运算符的扩展,是一种针对数组或者对象进行模式匹配,然后对其中的变量进行赋值。比如: let [a, b, c] = [1, 2, 3]; //a:1, b:2, c:3)

  9. for of

    forEach、for infor of三者区别forEach更多的用来遍历数组for in 一般常用来遍历对象或jsonfor of数组对象都可以遍历,遍历对象需要通过和Object.keys()for in循环出的是key,for of循环出的是value
    
  10. 模板

  11. 默认参数

    var link = function(height = 50, color = 'red', url = 'azat.co') { ...}