2017.12.11-学习笔记:js进阶知识点整理
前言:古人云,温故而知新,把自己学习js的进阶笔记整理一下,有错误之处还望指出,谢谢。
→点我去看js基础知识点整理
→点我去看jQuery知识点整理
→点我去看dom知识点整理
JavaScript进阶
1.typeof:
- 1.返回的是一个字符串;
- 2.只能判断简单数据类型,复杂数据类型用instanceof;
- 3.是一个关键字,不是一个函数,()的作用:提升优先级;
- 4.如果是一个函数,返回的是function,函数在js里是一等公民;
- 5.如果是null,返回的是object。
2.逻辑运算符:
-
逻辑中断:
&& : 找假值, 从左往右依次判断,如果某个操作数的逻辑是false,中断。
|| : 找真值, 从左右往右依次判断,如果某个操作的逻辑是true,中断。 -
转换成布尔类型是false的几种情况:
null undefined 0 NaN "" false-
补充:null 和 undefined
-
null 是一个表示“无”的对象,转为数值时为 0;undefined 是一个表示“无”的原始值,转为数值时是NaN
-
当声明的变量还未被初始化时,变量的默认值为 undefined。null 用来表示尚未存在的对象,常用来表示函数企图返回一个不存在的对象。
-
undefined 表示“缺少值”,就是此处应该有一个值,但是还没有定义。典型用法是:
1.变量被声明了,但没有赋值时,就等于 undefined
2.调用函数时,应该提供的参数没有提供,该参数等于 undefined
3.对象没有赋值的属性,该属性的值为 undefined
4.函数没有返回值,默认返回 undefined -
null 表示“没有对象”,即该处不应该有值。典型用法是:
1.作为函数的参数,表示该函数的参数不是对象
2.作为对象原型链的终点
-
-
补充:null 和 undefined
-
使用场景:
function animate(fn) {
fn && fn(); //如果传参数函数就执行函数
}
animate();
3.比较运算符:
- == 的转换规则:
- NaN 不等于任何值 包括NaN本身
- null 不等于任何值, 除了null 和 undefined
- undefined不等于任何值, 除了undefined本身和null
- 如果两边有布尔类型, 两边都转换成数值类型比较 false:0 true:1
- 如果两边有数值类型, 两边都转换成数值类型。
- 如果两边没有数字和布尔类型,有字符串,都转换成字符串比较。
- 如果两边都是对象,比地址。
Number( [ 空数组 ] )为 0 ;Number( { 空对象 } )为 NAN。
4.严格模式:
- 开启严格模式:’’use strict’’
1.变量必须声明;
2.禁止this指向window对象;
3.禁用八进制。
5.数据类型:
- 简单数据类型:值类型,存储的是值本身;
- 复杂数据类型:引用类型,存储的是对象的地址,复制的地址,对象只有一个,两个变量指向同一个对象。
6.拷贝:
-
浅拷贝:将对象中所有的属性都依次赋值到新对象里面去。浅拷贝只复制了一层对象的属性
如果对象中的属性还有对象,那浅拷贝只能复制该对象的地址,所以复制后该属性的对象和原属性的对象是同一个。 -
深拷贝:判断原对象的属性值的数据类型是否为对象
typeof obj[k] === 'object'
,
利用递归temp[k] = cloneObj(obj[k]);
实现。
7.对象:无序的键值对的集合。
-
面向过程:员工,一步一步考虑做事的过程;
面向对象:老板,让谁做什么事。
面向对象是面向过程的封装,而不是替代。 -
面向对象的三大特性:
-
1.封装:
函数:封装一段代码。
对象: 把一些属性和函数封装到一个对象了。 -
2.继承:
拿来主义:一个对象没有这个属性或者方法,另一个对象有个这个属性或者方法,通过某些手段,能够直接用另一个对象的属性和方法。 - 3.[多态]:js中没有,只有强类型语言有。
-
1.封装:
-
创建对象的方式:
var obj = new Object();
var obj={}; // 字面量创建;
//工厂函数:每次调用,都会创建出来一个对象。
function createPhone(brand) {
var obj = {
brand: brand
}
return obj;
}
// 缺点:创建出来的对象全是object类型,无法区分。
/*构造函数:实例化对象
1. 首字母大写
2. 作用: 实例化对象(给this添加属性和方法)
3. 配合new关键字使用,如不配合new,this指向window*/
function Phone(brand) {
this.brand = brand;
}
// 缺点:内存浪费;改进:用原型'
var hw = new Phone("华为");
-
new关键字:
- 在内存开辟一块新的空间,创建一个空的对象
- 让构造函数的this指向这个新的对象
- 调用构造函数
- 返回新对象。
8.prototype:原型对象
- 所有的函数(构造函数)都会自带一个属性:prototype 这个属性的值是一个对象: 原型对象。
- 通过构造函数实例化的对象 可以直接访问prototype属性(原型对象)的属性和方法。
-
构造函数中相同的属性和方法可以放入其对应的原型对象中。
对象: 存储每个不一样值 (name, age , gender)
原型:存储所有对象共同的东西(方法) -
所有的函数都会有一个属性:prototype 指向其 原型对象
所有的对象都会有一个属性:_proto_ 指向当前对象的构造函数的 prototype 属性。
所有的原型对象都会有一个属性:constructor 指向当前 构造函数 -
属性搜索原则:
如果自己有,用自己的,如果没有,找对象._proto_,如果还没有,一直往上找,知道Object.prototype,如果没有,undefined。
9.原型链:
-
一个对象会有一个原型(对象._proto_),同时这个原型也是一个对象,这个原型也会有原型,一环扣一环,就形成了一个链式的结构,我们把这个链式结构叫做;原型链。
-
对象可以使用原型上的东西,也可以使用原型的原型的东西,对象可以使用整个原型链上的东西。
1.var p = new Person();
p-- > Person.prototype-- > Object.prototype-- > null
2.var arr = [];
arr-- > Array.prototype-- > Object.prototype-- > null
3.var date = new Date();
date-- > Date.prototype-- > Object.prototype-- > null
4.var obj = {}
obj-- > Object.prototype-- > null
5. Math对象
Math-- > Object.prototype-- > null
10.Object.prototype的成员:
-
obj.hasOwnProperty(prop) :判断prop属性是否是obj自己提供
in操作符:如果属性不是自己提供的,是从原型上继承来的,也会返回true -
obj.propertyIsEnumerable(prop) : 判断prop是否是自己的属性, prop属性可否遍历
-
obj1.isPrototypeOf(obj2): 判断obj1是否在obj2原型链上。
-
toString(): Object.prototype.toString() [object type] 将对象转换成字符串
-
toLocaleString():跟本地化有关,
-
valueOf() : 将对象转换成简单类型(数值、布尔)
11.沙箱:匿名函数自调用
- 优点:不用担心全局变量污染的问题
缺点:完全与外界隔绝
对外暴露变量:传递window作为参数传入,window.变量=变量
12.继承:让一个对象可以使用另一个对象的属性和方法。
- 混入式继承:(jQuery的extend继承)
var jQuery = {
extend: function(temp) {
for (var k in temp) {
if(temp.hasOwnProperty(k)){
this[k] = temp[k];
}
}
}
}
var class = {
addClass:function(){}
}
jQuery.extend(class)
-
原型式继承:
一个对象可以访问构造函数的原型中的属性和方法,那么如果想要让一个对象增加某些属性和方法,只需要把这些属性和方法放到原型对象中即可。
// 1.直接给原型增加属性和方法
// 2.原型替换(jQuery的原型替换)
jQuery.prototype = {
constructor: jQuery,
version: "1.0.0",
extend: function(temp) {
for (var k in temp) {
if(temp.hasOwnProperty(k)){
this[k] = temp[k];
}
}
}
}
// 3.mixin+原型:不换原型,给原型混入
class = {
addClass:function(){}
}
jQuery.prototype.extend = function(temp) {
for (var k in temp) {
if(temp.hasOwnProperty(k)){
this[k] = temp[k];
}
}
}
jQuery.prototype.extend(class)
-
经典继承:
ES5提供了一个方法: Object.create(obj);
返回值:一个新的对象
参数: 一个对象,这个对象会成为新对象的原型对象
var lw = {
money: 1000000,
car: "劳斯莱斯"
}
var newObj = Object.create(lw);
- 如何安全的扩展内置对象:
需求:要给所有创建的数组增加一个方法,sayHi的方法
注意:永远不要去修改内置对象。
function MyArray() {}
MyArray.prototype = new Array() / [空数组];
MyArray.prototype.push = function() {
console.log("hehe");
}
var arr = new MyArray();
arr.push();
var arr2 = new Array();
console.log(arr2.push(1, 2, 3));
13.函数进阶
- 定义函数:
-
1.函数声明:function fn(){}
函数声明会提升,因此可以先调用,后声明
函数声明:必须要有名字 ()里面只能出现表达式
!function( ){ }( );
等同于匿名函数自调用 -
2.函数表达式:var fn = function() {}
函数表达式只会将函数名提升,所以只能在赋值后调用函数。 -
3.构造函数的方式 function是复杂类型
Object:内置构造函数,用来创建对象的
Function:内置构造函数,用来创建函数的
参数: arg1,arg2,arg3.....body
1. 所有的参数都是字符串
2. 前面可以指定任何多个形参,最后一个参数是代码体
-
var fn1 = new Function("a1", "a2", "alert(a1+a2)");
fn1(1,2);//3
14.eval的作用:
- 将一段字符串当成代码来执行。
eval("alert(1+2)");
- 代码编辑器:
button.onclick = function() {
var content = txt.value;
eval(content);
}
- eval 让一个json字符串转换成js对象。
15.json:
-
一种通过的数据交换的格式,就是一个字符串,只不过这个字符串有一定的规则。
var json = '{"name": "zs", "age": 18, "sex": "男"}'; -
把json字符串转换成js对象:
var obj = JSON.parse(json);
参数:json字符串
返回值:js对象 -
把js对象转换成json字符串:
var json = JSON.stringify(obj); -
eval也可以把一个json字符串转换成js对象(少用)
var json = '{"name": "zs", "age": 18, "sex": "男"}';
eval会把字符串当成js代码来解析
var obj = eval("(" + json + ")"); //{}不止有对象的意思,还有代码块的意思
16.this的指向:
-
函数:当一个函数不是一个对象的属性时,我们称之为函数。
方法:当一个函数被保存为对象的一个属性时,我们称之为方法。 -
this:
1.函数调用模式:this指向的是window
2.方法调用模式:this指向调用当前方法的对象
3.构造函数调用模式:如果函数是new调用的,此时this被绑定到创建出来的新对象上
4.上下文调用模式:call、apply- Call:
可以调用一个函数,并且可以指定这个函数的this指向
第一个参数可以指定函数内部的this指向
fn.call(thisArg, arg1, arg2, arg3);==fn(arg1, arg2, arg3) - Apply:
apply()方法的作用和 call()方法类似,只有一个区别,就是apply()方法接受的是数组或者伪数组, 里面存放了所有的参数列表。而call()方法接受的是若干个参数的列表。
apply有一个特性:平铺特性, apply会把数组里面的每一项都当成参数。
fn.apply(thisArg, [arg1, arg2, arg3]);==fn(arg1, arg2, arg3)
5.bind:创建一个新的函数, 可以绑定新的函数的this指向
返回值:新的函数
参数:新函数的this指向,当绑定了新函数的this指向后,无论使用何种调用模式,this都不会改变。
var newFn = fn.bind(window); - Call:
17.伪数组:
-
1.伪数组其实就是一个对象,但是跟数组一样,伪数组也会有length属性,也有0,1,2,3等属性。
-
2.伪数组并没有数组的方法,不能使用push/pop等方法
-
3.伪数组可以跟数组一样进行遍历,通过下标操作。
-
4.常见的伪数组:
arguments、document.getElementsByTagName
的返回值、jQuery对象 -
5.伪数组借用数组的方法:
Array.prototype/ [ ].push.call(arrLike, "赵六");
-
6.将伪数组转换成真数组:
var arr = Array.prototype/ [ ].slice.call(arrLike);
或者通过es6let arr = [...arrLike]
18.完整版原型链:
- 函数是由new Function创建出来的,因此函数也是一个对象, 所有的函数都是new Function的实例。
原型链神图.jpg
- 总结:
所有函数都是new Function创建出来的,因此所有函数._proto都是Function.prototype
所有对象都是new Object创建出来的,因此所有对象._proto都是Object.prototyp
19.ECMAScript族谱
ECMAScript族谱-
Function.prototype成员:
1.arguments:获取函数的实参,被函数内部的arguments替代了。//只能在函数内部用,es6中用rest代替
2.length:获取形参的长度
3.name:获取函数的名字,此属性不允许修改
4.caller:用于获取当前在函数是在哪个函数中调用的,已经被废弃了。
5.constructor:指向当前构造函数,Function
6.call:调用函数,重新指向this
7.apply:调用函数,重新指向this
8.bind:重新指向this,返回一个新的函数,不调用。
arguments使用场景://只能用于函数内部,相当于函数内部的属性
/*jQuery的css方法
设置单个样式:.css(name, value)
设置多个样式:.css(obj)
获取样式.css(name)*/
function css() {
if (arguments.length === 2) {
console.log("要执行设置单个样式的操作");
}
if (arguments.length === 1) {
var temp = arguments[0];
if (typeof temp === "object") {
console.log("要执行设置多个样式的操作");
}
if (typeof temp === "string") {
console.log("要执行的是获取样式的操作");
}
}
}
function max() {
return Math.max.apply(Math, arguments);
}
console.log(max(1, 2, 3, 4, 55, 6, 7, 8, 9));// 55
20.字面量和变量
字面量(直接量):从字面的意思就能看出来类型和值 55 "abc" true [] {},浏览器能够直接显示字面量。
变量:可以变化的量, 必须要先声明,才能使用, 如果变量没有声明,浏览器不认识,就会报错。
21.预解析:
- 1.函数优先,先提升函数,再提升变量
- 2.如果有同名的变量,会直接忽略
- 3.如果有同名的函数,会覆盖
22.作用域:
-
作用域:变量起作用的区域,作用域决定了 一个变量该定义在哪里,该如何获取
全局变量: 定义在函数外面的变量,全局变量,特点:在任何地方都可以访问
局部变量: 定义在函数内部的变量,局部变量,特点:只有在当前函数内部才可以使用 -
js的作用域: 静态作用域(词法作用域)
查找变量的时候,变量的在函数声明的时候,作用域就已经定下来了。
查找变量的时候,看的是函数的声明,不管哪里调用
如果函数声明是在代码块{}里面的话, 函数的名字会提升,但是函数体不提升。
不要在代码块里面声明函数。用函数表达式 -
全局作用域:只要页面不关闭(卸载),全局作用域会一直存在,全局变量
函数作用域:函数调用时,就会形成一个函数作用域,内部的变量都是局部变量,函数调用结束时,函数作用域就释放了,局部变量就释放。 -
变量的搜索原则:
从当前作用域向上找,如果一个变量不存在,获取这个变量的值会报错xxx is not defined;,给这个变量设置值,那么设置变量就是隐式全局变量。
函数传形参=函数内部var 形参。 -
面试题
//作用域面试题1:
var num = 10;
fn1();
function fn1() {
console.log(num);
var num = 20;
console.log(num);
}
console.log(num);
//作用域面试题2:
var num1 = 10;
var num2 = 20;
function fn(num1) {
num1 = 100;
num2 = 200;
num3 = 300;
console.log(num1); //100
console.log(num2); //200
console.log(num3); //300
var num3;
}
fn();
console.log(num1); //10 100
console.log(num2); //200
console.log(num3); //报错
//作用域面试题3:
fn3();
console.log(c); //9 undefined
console.log(b); //9
console.log(a); //报错
function fn3() {
var a = b = c = 9;
console.log(a); //9
console.log(b); //9
console.log(c); //9
}
//作用域面试题4:
var num = 1;
function fn() {
var num = 100;
num++;
console.log(num);
}
fn(); //101
fn(); //101
console.log(num); //1
//作用域面试题5:
var color = "red";
function outer() {
var anotherColor = "blue";
function inner() {
var tmpColor = color;
color = anotherColor;
anotherColor = tmpColor;
console.log(anotherColor);
}
inner();
}
outer();
console.log(color); //red blue
//This面试题1:
var obj = {
sayHi: function() {
console.log(this);
}
}
var fn = obj.sayHi;
fn(); //请问打印结果是什么?
//This面试题2:
var fn = function() {
console.log(this);
}
var obj = {
sayHi: fn
}
//This面试题3:
var age = 38;
var obj = {
age: 18,
getAge: function() {
console.log(this.age); //???
function foo() {
console.log(this.age); //????
}
foo();
}
}
obj.getAge();
23.递归函数:函数内部直接或者间接的调用自己
- 函数直接或者间接自己调用自己
- 递归一定要有结束的条件(死递归)
- 化归思想:
// 1.计算1-n之间所有数的和
function getSum(n) {
if (n == 1) {
return 1;
}
return getSum(n - 1) + n;
}
console.log(getSum(100));
// 2.计算斐波那契数列
var arr = [];
function fib(n) {
if (n == 1 || n == 2) {
return 1;
}
return fib(n - 1) + fib(n - 2);
}
console.log(fib(100));
var n1 = 1;
var n2 = 1;
var num = 0;
for (var i = 3; i <= 12; i++) {
num = n1 + n2;
n1 = n2;
n2 = num;
}
console.log(num);
- 递归实现深拷贝
24.闭包:
-
为何需要闭包:
在js中,函数会形成函数作用域,在函数内部可以直接访问全局变量,在函数外部却无法访问函数内部的变量;在函数内部有一个函数,那么函数内部的函数是可以访问到外部函数的变量的。闭包就是能够读取到其他函数内部变量的函数。 -
定义在函数内部的函数,用来连接函数内部与外部的桥梁。
-
闭包应用:
//1.计数器:
function outer() {
var count = 0;
function add() {
count++;
console.log("当前count" + count);
}
return add;
}
var result = outer();
result();
//2.私有变量的读取和设置:
function outer() {
var num = 10;
function set_num(n) {
num = n;
}
function get_num() {
return num;
}
return {
set_num: set_num,
get_num: get_num
}
}
var obj = outer();
obj.set_num(2000);
console.log(obj.get_num());
//3.实现缓存:
//缓存(cache):数据的缓冲区,当要读取数据时,先从缓冲中获取数据,
//如果找到了,直接获取,
//如果找不到,重新去请求数据。
function outer() {
//缓存
var arr = [];
var fbi = function(n) {
if (n == 1 || n == 2) {
return 1;
}
if (arr[n]) {
return arr[n];
} else {
var temp = fbi(n - 1) + fbi(n - 2);
arr[n] = temp; //存入缓存
return temp;
}
}
return fbi;
}
var fbi = outer();
console.log(fbi(40));
- Js垃圾回收机制:
引用记数垃圾收集:如果没有引用指向某个对象(或者是函数作用域),那么这个对象或者函数作用域就会被垃圾回收机制回收。
var o = {
name:"zs"
}
//对象被o变量引用 ,引用记数1
var obj = o; //变量被o和obj引用,引用记数2
o = 1; //o不在引用对象了, 引用记数1
obj = null; //obj不在引用对象了,引用记数0,可以被垃圾回收了。
闭包占用内存释放:
function outer(){
var count = 0;
function fn(){
count++;
console.log("执行次数"+count);
}
return fn;
}
var result = outer();
result();
result = null;//当函数fn没有被变量引用了,那么函数fn就会被回收,
//函数fn一旦被回收,那么outer调用形成的作用域也就得到了释放。
- 闭包面试题:
打印当前按钮对应的下标:
第一种:使用let定义变量 es6 let特点:{}也能形成作用域
var btns = document.querySelectorAll("button");
for (let i = 0; i < btns.length; i++) {
btns[i].onclick = function () {
console.log(i);
}
}
第二种: 使用属性存储下标
var btns = document.querySelectorAll("button");
for (var i = 0; i < btns.length; i++) {
btns[i].index = i;
btns[i].onclick = function () {
console.log(this.index);
}
}
第三种: 使用forEach遍历
var btns = document.querySelectorAll("button");
btns.forEach(function (e, i) {
//e:是每一个元素 i:每一个下标
btns[i].onclick = function () {
console.log(i);
}
});
第四种:闭包解决
var btns = document.querySelectorAll("button");
for (var i = 0; i < btns.length; i++) {
(function (num) {
btns[num].onclick = function () {
console.log(num);
}
})(i);//调用btns.length-1次匿名函数,每次调用都产生相应的函数作用域,
//在每个相应的函数作用域中给注册事件赋值一个函数
}
第五种:
var btns = document.querySelectorAll("button");
for (var i = 0; i < btns.length; i++) {
btns[i].onclick = (function (num) {
function fn() {
console.log(num);
}
return fn;
})(i);//给btn注册btns.length-1次事件赋值,每次注册调用匿名函数产生相应的函数作用域
}
使用setTimeout 每秒钟打印一个数字 0, 1, 2,3,4,5,6,7,8,9
第一种:let
for(let i = 0; i < 10; i++){
setTimeout(function () {
console.log(i);
}, i * 1000 )
}
第二种:闭包
for(var i = 0; i < 10; i++){
(function (num) {
setTimeout(function () {
console.log(num);
}, num * 1000)
})(i);
}
第三种:闭包
for(var i = 0; i < 10; i++){
var fn = (function (i) {
return function(){
console.log(i);
}
})(i);
setTimeout(fn, i * 1000);
}
25.正则表达式:js中常用于表单验证。
-
创建:
1.构造函数的方式:var regExp = new RegExp(/\d/);
2.正则字面量:var regExp = /\d/;
3.正则的使用:/\d/.test("aaa1"); -
普通字符:abc
-
元字符:具有特殊意义的字符
\d: 数字
\D: 非数字
\w: 单词字符 a-zA-Z0-9_
\W: 非单词字符
\s: 不可见字符 空格 \t \n
\S: 可见字符
. : 任意字符 (\n)
\.: 匹配.
|: 或 优先级最低
(): 优先级最高的
[]: 一个字符的位置,里面是可以出现的字符
[^] :^表示非
^表示开始
$表示结束
^$ 表示精确的匹配
*表示 出现0次或者0次以上
+ 表示 1次或者1次以上
?表示 0 次 或者 1次
{n,m} 出现n次到m次 * {0,} + {1,} {0,1}
{n,} 出现n次或者n次以上
{n} 出现n次 -
常见验证:
var phoneReg = /^0\d{2,3}-\d{7,8}$/;
var qqReg = /^[1-9]\d{4,11}$/;
var mobileReg = /^1[345789]\d{9}$/;
var nameReg = /^[\u4e00-\u9fa5]{2,4}$/;
var emailReg = /^\w+@\w+(.\w+)+$/;
-
正则的参数: /\d/g :global 全局的 i: ignore:忽略大小写
-
字符串替换:
题1:var str = " 123AD asadf asadfasf adf ";
①.把所有 ad 换成 xx
str = str.replace(/ad/g, "xx");
②.把ad /AD /aD /Ad 都换成xx
str = str.replace(/ad/gi, "xx");
③.把str中所有的空格去掉
str = str.replace(/\s/g, "");
题2:var str = "abc,efg,123,abc,123,a";
所有的逗号替换成 !
str = str.replace(/,/g, "!");
题3:var jsonStr = '[{"name":"张三",score:8},{"name":"张三",score:90},{"name":"张三",score:81}]';
把所有成绩都修改成100分
jsonStr = jsonStr.replace(/\d{1,2}/g, "100");
字符串传匹配,匹配所有分数
var reg = /\d{1,2}/g;
var arr = jsonStr.match(reg);
console.log(arr);
题4:var str = "大家好,我是xxx,我的手机号码是18511112222,我前女友的手机号码是13222221111,我的现女友的手机号码15555555555";
var reg = /1[345789]\d{9}/g;
var arr = str.match(reg);
console.log(arr);