JavaScript 进阶技能,中高级前端必备

JavaScript 高级

作者: 写代码的小鱼儿

Email:282505458@qq.com

QQ:282505458

微信:15311464808

说明:本文档用于学习交流,可传播可分享,如有错误,请联系小鱼儿,感谢矫正与探讨

创建对象的三种方式

对象字面量

对象名 = {}
var obj = {
	name : '张三',
    age : 20,
    say : function () {
        console.log('say...');
    }
}

系统构造函数,工厂模式

对象名 = new Object();
function createObject (name,age) {
	var obj = new Object();
    obj.name = name;
    obj.age = age;
    obj.say = function () {
        console.log('say....');
    }
}

自定义构造函数

原理同工厂模式,方法名首字母大写。自定义构造函数自动做了四件事:

a: 在内存中创建新的对象

b: this 指向这个对象

c: 给这个对象添加属性和方法

d: 返回这个新对象

function Person (name,age) {
    this.name = name;
    this.age = age;
    this.say = function () {
        console.log('say...');
    }
}

检测对象是否是某个构造函数实例化的结果

(1)对象.instanceof.构造方法

(2)对象.constructor == 构造方法

工厂模式创建对象与构造函数创建对象的区别

工厂模式:

函数名小驼峰命名法,函数内部有new,返回值为所创建对象。直接调用函数创建对象。实例对象的构造类都是Object,不方便区分是哪一类。

构造函数:

函数名大驼峰命名法,函数内部没有new,没有返回值,this代指当前对象,通过new的方式创建对象。方便区分是某类的对象,可以用instanceof检测

构造函数与实例对象的区别

function Person(name, age, sex){    
    this.name = name;    
    this.age = age;    
    this.sex = sex;    
    this.say = function(){        
        console.log("say....");    
    }; 
}
var p = new Person("aa", 13, "男"); 
//构造函数与实例对象之间的关系 
//实例对象是通过构造函数创建出来的 
//console.dir打印对象结构 
console.dir(p); 
//打印实例对象 
console.dir(Person); 
//打印构造函数
//constructor实例对象的构造器 指向的是Person 所以这个实例对象是通过Person来创建的 
console.log(p.__proto__.constructor == Person.prototype.constructor); console.log(p.constructor == Person);

结论:

实例对象是通过构造函数创建的 可以通过 实例对象.构造器 == 构造函数名 来判断对象是不是这个构造函数创建的 对象 instanceof 构造函数名 一般使用这种方式

原型

javascript中的每个对象都有prototype属性,Javascript中对象的prototype属性的解释是:返回对象类型原型的引用每一个构造函数都有一个属性叫做原型。这个属性非常有用:为一个特定类声明通用的变量或者函数。
你不需要显式地声明一个prototype属性,因为在每一个构造函数中都有它的存在。

prototype 既是属性也是对象。
每个函数都有一个 prototype 属性,被称为原型对象,原型对象中的属性和方法可以通过实例对象访问。

每个实例对象都有一个 __ proto__ 属性,指向该实例对象的构造函数。
可以根据这两个属性来判断该元素是函数还是对象。

原型的引入

//构造函数  构造函数名Person
function Person(name, age){
    this.name = name;
    this.age = age;
    this.say = function(){
        console.log("say");
    };
}
//得到一个实例对象
var p1 = new Person("aa", 34);
var p2 = new Person("bb", 45);
//这两个方法是否是同一个方法? 不是同一个方法
p1.say();
p2.say();

结论:

通过上述方式创建的对象,分别拥有自己的属性和方法。如果多个实例对象拥有同样的方法,采用这种方式比较浪费空间,可以采用原型对象的方式,将共有的属性和方法存储在原型对象中,实现空间共享,节省空间。

原型的作用

数据共享

在构造函数中定义的属性和方法,在实例对象的时候,实例对象的属性和方法都是在自己的空间中存在的。如果实例化多个对象,那么这些属性和方法都会存储在单独的空间,为了节省内存空间,我们会把对象共有的属性或方法写在原型对象中,实现节省内存空间。

改变this指向

原型对象中的this指向为实例对象,指向可以改变。

构造函数、实例、原型之间的关系

Javascript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象。
这个对象的所有属性和方法,都会被构造函数的实例继承。
这也就意味着,我们可以把所有对象实例需要共享的属性和方法直接定义在 prototype 对象上。

这时所有实例的 type 属性和 sayName() 方法,
其实都是同一个内存地址,指向 prototype 对象,因此就提高了运行效率。

任何函数都具有一个 prototype 属性,该属性是一个对象。

通过构造函数得到的实例对象内部会包含一个指向构造函数的 prototype 对象的指针 proto

总结:
任何函数都具有一个 prototype 属性,该属性是一个对象
构造函数的 prototype 对象默认都有一个 constructor 属性,指向 prototype 对象所在函数
通过构造函数得到的实例对象内部会包含一个指向构造函数的 prototype 对象的指针
proto
所有实例都直接或间接继承了原型对象的成员

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GkotGMpW-1577673827666)(/home/yujing/.config/Typora/typora-user-images/image-20191119103004205.png)]

通过原型对象添加方法

构造函数.prototype.属性 = 值;
构造函数.prototype.方法 = 值;

通过原型对象添加属性可以简写为:
构造函数.prototype = {
手动加构造器
}

//构造函数  构造函数名Person
function Person(name, age){
this.name = name;
this.age = age;
}

//使用原型添加方法
Person.prototype.say = function(){
	console.log("say....");
};

//判断两个方法是否是同一个方法
console.log(p1.say == p2.say); //true

结论:

使用原型对象创建属性和方法,在实例化对象中看不到这个属性或方法。

原型对象

实例对象中有一个__proto__的属性,就是原型,也是一个对象,这个属性是给浏览器使用,不是标准属性,__proto__可以叫原型对象

构造函数中有一个prototype的属性,就是原型,也是一个对象,这个是标准属性,这个属性是程序使用的。prototype可以叫原型对象

实例对象中的__proto__属性与构造函数中的prototype属性相等

实例对象是通过构造函数创建出来的,构造函数中有prototype原型对象

实例对象__proto__指向了构造函数中的原型对象

通过原型添加属性和方法

Person.prototype.classid = "220";
Person.prototype.eat = function(){
	console.log("吃盒饭.....");
};
Person.prototype.study = function(){
	console.log("吃饭睡觉打豆豆,就是不学习,因为我不学就会");
};

以上代码可以简写:

需要注意的是,需要手动添加 constructor 构造器

//使用原型添加属性和方法
Person.prototype = {
    //手动添加构造器
    constructor: Person,
    classid: "220",
    eat: function(){
        console.log("吃饭的方法");
    }
};

总结:实例对象与原型对象的关系

构造函数可以实例化对象。

构造函数中有一个属性叫prototype,是构造函数的原型对象。

构造函数的原型对象(prototype)中有一个constructor构造器,这个构造器指向的就是自己所在的原型对象所在的构造函数。

实例对象的原型对象(proto)指向的是该构造函数的原型对象。

实例对象属性与原型属性重名问题

当访问实例对象的某个属性的时候,首先会在实例对象的构造函数中寻找,找到了就直接使用,找不到就会按照原型链向原型对象中寻找。如果找不到,属性会返回 undefined 方法会返回 not a function

通过实例对象可以改变原型对象中的属性值吗?

不可以。如果想改变原型对象中属性的值,可以直接通过 原型对象.属性 = 值 的方式改变。

原型对象使用建议

私有成员(一般就是非函数成员)放到构造函数中
共享成员(一般就是函数)放到原型对象中
如果重置了 prototype 记得修正 constructor 的指向

可以为系统内置对象添加原型方法,相当于再改源码

系统内置对象的属性和方法可能不能满足需要,可以通过原型的方式加入属性和方法,为了方便开发
为内置对象的原型添加属性和方法,那么这个内置对象的实例对象就可以直接使用

String.prototype.myReverse=function () {      
    for(var i=this.length-1;i>=0;i--){        
        console.log(this[i]);      
    }    
};    
var str="abcdefg";    
str.myReverse();

构造函数与实例对象的关系

  • 实例对象是由构造函数所创建的;

  • 实例对象中的 proto 中的 constructor指向构造函数
    构造函数中的 constructor存储构造函数本身

验证一个实例对象是否是由某个构造函数产生的

​ 对象 instanceof 构造方法 判断 建议用这个
​ 对象.constructor = 构造方法名 返回布尔值

原型中的方法是可以互相访问的

function Animal(name,age) {      
    this.name=name;      
    this.age=age;    
}    
//原型中添加方法    
Animal.prototype.eat=function () {      
    console.log("动物吃东西");      
    this.play();    
};    
Animal.prototype.play=function () {      
    console.log("玩球");      
    this.sleep();    
};    
Animal.prototype.sleep=function () {      
    console.log("睡觉了");
}

注意:

实例化对象与原型属性重名时,先寻找对象本身的属性,对象本身没有时再采用原型的属性,都没有时属性返回undefined,方法返回not a function

function Person(age,sex) {     
    this.age=age;//年龄     
    this.sex=sex;     
    this.eat=function () {      
        console.log("构造函数中的吃");    
        };   
}   
Person.prototype.sex="女";   
Person.prototype.eat=function () {    
	console.log("原型对象中的吃");   
};   
var per=new Person(20,"男");   
console.log(per.sex);  //男 
per.eat();//构造函数中的吃

原型链

原型链:是一种关系,实例对象和原型对象之间的关系,他们的关系 是通过原型 __ proto__ 来联系的。
每个构造函数都有一个 prototype 属性,每个实例对象都有一个 __ proto__ 属性,而 __ proto__ 属性指向构造函数的 prototype 属性。按照__ proto__ 寻找原型链的构造函数,会发现实例对象的顶端是 Object ,再向上寻找__ proto__ 会返回null。

//构造函数 
function Person(name,age) {  
    //属性  
    this.name=name;  
    this.age=age;  
    //在构造函数中的方法  
    this.eat=function () {    
        console.log("吃吃吃");  
    }; 
} 
//添加共享的属性 
Person.prototype.sex="男"; 
//添加共享的方法 
Person.prototype.sayHi=function () {  
    console.log("您好啊,"); };
//实例化对象,并初始化 
var per=new Person("小明",20); 
per.sayHi(); 
//如果想要使用一些属性和方法,并且属性的值在每个对象中都是一样的,方法在每个对象中的操作也都是一 样,那么,为了共享数据,节省内存空间,是可以把属性和方法通过原型的方式进行赋值
console.dir(per);
//实例对象的结构 
console.dir(Person);
//构造函数的结构
//实例对象的原型__proto__和构造函数的原型prototype指向是相同的
//实例对象中的__proto__原型指向的是构造函数中的原型prototype 
console.log(per.__proto__==Person.prototype); 
//实例对象中__proto__是原型,浏览器使用的 //构造函数中的prototype是原型,程序员使用的

原型的指向是可以改变的

实例对象的原型 __ proto __ 指向的是该对象所在的构造函数的原型对象。如果构造函数的原型对象 prototype 指向发生了改变,实例对象的原型 __ proto__ 指向也会发生改变。实例对象和原型对象之间是通过 __ proto__ 原先来联系的,这个关系就是原型链。

//人的构造函数 
function Person(age) {  
    this.age=10; 
} 
//人的原型对象方法 
Person.prototype.eat=function () {  
    console.log("人的吃"); 
}; 
//学生的构造函数 
function Student() {
    } 
Student.prototype.sayHi=function () {  
    console.log("嗨,小苏你好帅哦"); 
}; 
//学生的原型,指向了一个人的实例对象 
Student.prototype=new Person(10); //改变了原型的指向
var stu=new Student(); 
stu.eat(); stu.sayHi();

原型的最终指向问题

Javascript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象。
这个对象的所有属性和方法,都会被构造函数的实例继承。
这也就意味着,我们可以把所有对象实例需要共享的属性和方法直接定义在 prototype 对象上。
prototype是对象,所以prototype这个对象中也有 proto ,指向了哪儿里?(对象中 proto 指 向的都是构造函数的prototype) 所以prototype这个对象中的 proto 指向的应该是某个构造函数的原型prototype

function Person() { 
} 
Person.prototype.eat=function () {  
    console.log("吃东西"); 
};
var per=new Person(); 
console.dir(per); 
console.dir(Person); 

//per实例对象的__proto__------->Person.prototype的__proto__---->Object.prototype的 __proto__是null
console.log(per.__proto__==Person.prototype); console.log(per.__proto__.__proto__==Person.prototype.__proto__); console.log(Person.prototype.__proto__==Object.prototype); console.log(Object.prototype.__proto__);

原型指向改变如何添加方法

如果原型的指向发生改变,那么应该在原型改变指向之后再添加原型方法。

//人的构造函数 
function Person(age) { 
    this.age=age; 
} 
//人的原型中添加方法 
Person.prototype.eat=function () { 
    console.log("正在吃东西"); 
}; 
//学生构造函数 
function Student(sex) { 
    this.sex=sex; 
} 
//改变了原型对象的指向  
Student.prototype=new Person(10);
//学生的原型中添加方法----先在原型中添加方法  
Student.prototype.sayHi=function () {  
    console.log("哈喽哈喽");  
};  
var stu=new Student("男");  
stu.eat();  
stu.sayHi();
console.dir(stu);

继承

JS 不是一门面向对象的语言,它是一门基于对象的语言。我们学习 JS 的面向对象,是英文面向对象的思想适合人的想法,编程会更加方便,也利于后期的维护。

继承:首先继承是一种关系。

可以通过构造函数模拟类,然后通过改变原型指向来实现继承。继承也是为了实现数据共享。

缺陷:通过改变原型指向实现的继承,直接初始化了属性,继承过来的属性的值都是一样的。为了解决这一个问题,我们可以使用 call 函数。(指构造函数中的属性值不能改变)

继承的时候,可以不改变原型的指向,直接调用父级的构造函数的方式来为属性赋值就可以了。借用构造函数,把要继承的父级的构造函数拿过来,使用一下就可以了。

要借用的构造函数.call(要绑定函数的对象 [, 函数的参数1] [, 函数的参数2…] )

缺陷:父级中的方法不能继承。(指原型对象中的成员不能继承)

所以,为了完美实现继承,我们采用 原型继承 + 借用构造函数 来实现继承。

原型继承 + 借用构造函数 来实现继承

function Person(name,age,sex) {  
    this.name=name;  
    this.age=age;  
    this.sex=sex; 
} 
Person.prototype.sayHi=function () {  
    console.log("哈喽"); 
}; 
function Student(name,age,sex,score) {  
    //借用构造函数:属性值重复的问题  
    Person.call(this,name,age,sex);  
    this.score=score; 
} 
//改变原型指向----继承 
Student.prototype=new Person();
//不传值 //改变原型指向后添加方法 
Student.prototype.eat=function () {  
    console.log("吃东西"); 
}; 
var stu=new Student("小黑",20,"男","100分"); console.log(stu.name,stu.age,stu.sex,stu.score); 
stu.sayHi(); 
stu.eat(); 
var stu2=new Student("小黑黑",200,"男人","1010分"); console.log(stu2.name,stu2.age,stu2.sex,stu2.score); 
stu2.sayHi(); 
stu2.eat();

拷贝继承

把一个对象的竖向或方法通过遍历对象 直接复制到另一个对象中。

function Person() {} 
Person.prototype.age=10; 
Person.prototype.sex="男"; 
Person.prototype.height=100; 
Person.prototype.play=function () { 
    console.log("玩的好开心"); 
}; 
var obj2={}; 
//Person的构造中有原型prototype,prototype就是一个对象,那么里面,age,sex,height,play都是 该对象中的属性或者方法 
for(var key in Person.prototype){ 
    obj2[key]=Person.prototype[key]; 
} 
console.dir(obj2); 
obj2.play();

总结继承:

原型继承:改变原型的指向

借用构造函数继承:主要解决属性值不可改变的问题

原型继承 + 借用函数继承,既能解决属性的问题,又能解决方法继承的问题。

拷贝继承:就是把对象中需要共享的属性或方法,通过遍历的方式复制到另一个对象中。

函数

函数的声明方式

(1)函数声明的方式

​ function 函数名 () {}

(2)函数表达式

​ function () {}

函数声明和函数表达式的区别:

函数声明如果放在 if… else 分支结构中,在IE8 中会出现问题

//函数声明方式
if(true){    
    function f1() {        
        console.log("真区间函数");    
    } 
}else{
    function f1() {        
        console.log("假区间函数");    
    } 
} 
f1();//IE8 下返回 ‘假区间函数’

//函数表达式方式
var f2; 
if(true){    
    f2=function () {    
        console.log("函数表达式--真区间");    
    }; 
}else{    
    f2=function () {    
        console.log("函数表达式--假区间");    
    }; 
} 
f2();//所有浏览器都返回 "函数表达式--真区间"

函数中 this 指向

应用环境this指向
普通函数window
对象方法当前实例对象
定时器方法window
构造函数实例对象
原型对象方法实例对象
//普通函数中
this function f1() {    
    console.log(this); 
} f1(); 

//定时器中的this 
setInterval(function () {    
    console.log(this); 
},1000);

//构造函数中this 
function Person() {    
    console.log(this);    
    //对象方法中this    
    this.sayHi=function () {    
        console.log(this);    
    }; 
} 

//原型中的方法中this 
Person.prototype.eat=function () {    
    console.log(this); 
}; 
var per=new Person(); 
console.log(per); 
per.sayHi(); 
per.eat();

//严格模式 "use strict";
function f1() {  
    console.log(this);
    //window  
} 
f1();

函数的不同调用方式

//普通函数 
function f1() {    
    console.log("普通函数"); 
} 
f1(); 

//构造函数---通过new 来调用,创建对象 
function F1() {  
    console.log("我是构造函数"); 
} 
var f=new F1();

//对象的方法 
function Person() {  
    this.play=function () {    
        console.log("玩玩玩");  
    }; 
} 
var per=new Person(); 
per.play();

函数是对象,对象不一定是函数

对象中有 proto 原型,函数中有prototype原型,如果一个结构里既有prototype,又有 proto , 说明是函数,也是对象

//对象中有__proto__原型,是对象 //函数中有prototype原型,是对象 
function F1() { } 

//函数中有prototype,也有__proto__ 
console.dir(F1);

//Math是对象  但不是函数没有prototype 
console.dir(Math);

//所有的函数实际上都是Function的构造函数创建出来的实例对象 
var f1=new Function("num1","num2","return num1+num2"); 
console.log(f1(1,2)); 
console.log(f1.__proto__==Function.prototype);
console.dir(f1); 
console.dir(Function);

apply和call方法的使用

apply的使用语法

函数名字.apply(调用该函数的对象,[参数1,参数2,…]);
方法名字.apply(对象,[参数1,参数2,…]);

call的使用语法

函数名字.call(对象,参数1,参数2,…);
方法名字.call(对象,参数1,参数2,…);

作用:改变this的指向

不同的地方:参数传递的方式是不一样的,apply参数是数组 ,call是参数列,逗号分隔

只要是想使用别的对象的方法,并且希望这个方法是当前对象的,那么就可以使用apply或者是call的方法改变this的指向

注意:

apply和call方法中如果没有传入参数,或者是传入的是null,那么调用该方法的函数对象中的this就是 默认的window

//调用函数 
function f1(x,y) {    
    console.log("结果是:"+(x+y)+this);    
    return "test"; 
}
f1(10,20);
//函数的调用
f1.apply(); 
f1.call();
f1.apply(null); 
f1.call(null); 
//apply和call方法中如果没有传入参数,或者是传入的是null,那么调用该方法的函数对象中的this就是 默认的window 
f1.apply(null,[100,200]); 
f1.call(null,100,200); 
var result1=f1.apply(null,[10,20]); 
var result2=f1.call(null,10,20); 
console.log(result1);
console.log(result2);

bind() 复制

使用的语法:

函数名字.bind(对象,参数1,参数2,…);---->返回值是复制之后的这个函数
方法名字.bind(对象,参数1,参数2,…);---->返回值是复制之后的这个方法

复制了一份的时候,把参数传入到了f1函数中,x== >10,y==>20,null就是this,默认就是window bind方法是复制的意思,参数可以在复制的时候传进去,也可以在复制之后调用的时候传入进去 apply和call是调用的时候改变this指向 bind方法,是复制的时候,改变了this的指向

function f1(x, y) {    
	console.log((x + y) + ":=====>" + this.age); 
} 
function Person() {    
    this.age = 1000; 
} 
Person.prototype.eat = function () { 
    console.log("这个是吃"); 
}; 
var per = new Person(); 
var ff = f1.bind(per, 10, 20); 
ff();

函数中的几个成员

name:函数的名字,name 属性是只读的,不能修改

arguments:实参 arguments[i] 实参的值 arguments.length 实参的个数

length:形参的个数

caller:函数的调用者

闭包

闭包的概念:函数A中,有一个函数B,函数B中可以访问函数A中定义的变量或者是数据,此时形成了闭包(这 句话暂时不严谨)

闭包的模式:函数模式的闭包,对象模式的闭包

闭包的作用:缓存数据,延长作用域链

**闭包的优点和缺点:**缓存数据

//函数模式的闭包:在一个函数中有一个函数
function f1() {    
    var num=10;    
    //函数的声明    
    function f2() {     
        console.log(num);    
    }    
    //函数调用    
    f2(); 
} 
f1();//10
//对象模式的闭包:函数中有一个对象 
function f3() {    
    var num=10;    
    var obj={    
        age:num   
    };    
    console.log(obj.age);//10 
} 
f3();
 //普通的函数    
function f1() {      
    var num = 10;      
    num++;      
    return num;    
}    
console.log(f1());    
console.log(f1());   
console.log(f1());         
//函数模式的闭包    
function f2() {      
    var num = 10;      
    return function () {        
        num++;        
        return num;     
 	}
}    
var ff = f2();    
console.log(ff());    //11
console.log(ff());    //12
console.log(ff());	  //13

总结:

如果想要缓存数据,就把这个数据放在外层的函数和里层的函数的中间位置 闭包的作用:缓存数据.优点也是缺陷,没有及时的释放 局部变量是在函数中,函数使用结束后,局部变量就会被自动的释放 闭包后,里面的局部变量的使用作用域链就会被延长

沙箱

环境,在一个虚拟的环境中模拟真实世界,做实验,实验结果和真实世界的结果是一样,但是不会影响真实世 界。

var num=10;   
console.log(num+10);
//沙箱---小环境   
(function () {     
    var num=10;     
    console.log(num);   
})();

//沙箱---小环境   
(function () {     
    var num=20;     
    console.log(num+10);   
}());
var num=100;   
(function () {     
    var num=10;     
    console.log(num);//10   
}());
   console.log(num);//100

递归

函数中调用函数自己,此时就是递归,递归一定要有结束的条件。

//递归实现:求n个数字的和   n=5--->  5+4+3+2+1 
//函数的声明 
function getSum(x) {    
    if(x==1){     
        return 1;    
    }    
    return x+getSum(x-1); 
} 
//函数的调用 
console.log(getSum(5));
//递归案例:求斐波那契数列
function getFib(x) {      
    if(x==1||x==2){        
        return 1      
    }      
    return getFib(x-1)+getFib(x-2);    
}    
console.log(getFib(12));

浅拷贝

拷贝就是复制,就相当于把一个对象中的所有的内容,复制一份给另一个对象,直接复制,或者说,就是把一个 对象的地址给了另一个对象,他们指向相同,两个对象之间有共同的属性或者方法,都可以使用

var obj1={      
     age:10,      
     sex:"男",      
     car:["benchi","hafu","baoma","aodi"]   
};    
//另一个对象    
var obj2={};        
//写一个函数,作用:把一个对象的属性复制到另一个对象中,浅拷贝    
//把a对象中的所有的属性复制到对象b中    
function extend(a,b) {      
    for(var key in a){        
        b[key]=a[key];      
    }    
}    
extend(obj1,obj2);    
console.dir(obj2);    
console.dir(obj1);

深拷贝

拷贝还是复制,深:把一个对象中所有的属性或者方法,一个一个的找到.并且在另一个对象中开辟相应的空 间,一个一个的存储到另一个对象中。两者内存地址不同。

var obj1={      
    age:10,      
    sex:"男",      
    car:["benchi","hafu","baoma","aodi"],      
    dog:{        
        name:"大黄",        
        age:5,        
        color:"黑白色"      
    }    };
var obj2={};//空对象    
//通过函数实现,把对象a中的所有的数据深拷贝到对象b中    
function extend(a,b) {      
    for(var key in a){        //先获取a对象中每个属性的值        var item=a[key];        //判断这个属性的值是不是数组        
if(item instanceof Array){          
    //如果是数组,那么在b对象中添加一个新的属性,并且这个属性值也是数组          
    b[key]=[];          
    //调用这个方法,把a对象中这个数组的属性值一个一个的复制到b对象的这个数组属性中          		     extend(item,b[key]);        
}else if(item instanceof Object){
    //判断这个值是不是对象类型的          
    //如果是对象类型的,那么在b对象中添加一个属性,是一个空对象         
    b[key]={};
       //再次调用这个函数,把a对象中的属性对象的值一个一个的复制到b对象的这个属性对象中	          extend(item,b[key]);        
}else{          //如果值是普通的数据,直接复制到b对象的这个属性中          
    b[key]=item;        
		}      
    }    
}
extend(obj1,obj2);    
console.dir(obj1);    
console.dir(obj2);

浅拷贝与深拷贝的区别:

1.浅拷贝: 将原对象或原数组的引用直接赋给新对象,新数组,新对象/数组只是原对象的一个引用

2.深拷贝: 创建一个新的对象和数组,将原对象的各项属性的“值”(数组的所有元素)拷贝过来,是“值”而不是“引用”

正则表达式

也叫规则表达式,按照一定的规则组成的一个表达式,这个表达式的作用主要是匹配字符串的,

正则表达式的作用:匹配字符串的

在大多数编程语言中都可以使用 正则表达式的组成:是由元字符或者是限定符组成的一个式子

元字符

.  表示的是:除了\n(换行)以外的任意的一个字符   "fdsfs238" 
[] 表示的是:范围,  [0-9] 表示的是0到9之间的任意的一个数字,  "789" [0-9] 
[1-7] 表示的是1到7之间的任意的一个数字 
[a-z] 表示的是:所有的小写的字母中的任意的一个 
[A-Z] 表示的是:所有的大写的字母中的任意的一个 
[a-zA-Z] 表示的是:所有的字母的任意的一个 
[0-9a-zA-Z] 表示的是: 所有的数字或者是字母中的一个 
[] 另一个函数: 把正则表达式中元字符的意义干掉   
[.] 就是一个. 
| 或者    
[0-9]|[a-z] 表示的是要么是一个数字,要么是一个小写的字母 
() 分组 提升优先级   [0-9]|([a-z])|[A-Z] 
([0-9])([1-5])([a-z]) 三组, 从左边开始计算
都是元字符,但是也可以叫限定符,下面的这些
* 表示的是:前面的表达式出现了0次到多次 
[a-z][0-9]* 小写字母中的任意一个 后面是要么是没有数字的,要么是多个数字的 "fdsfs3223323"  

+ 表示的是:前面的表达式出现了1次到多次 [a-z][9]+  小写字母一个后面少一个9,或者多个9   "fesfewww9fefds"

?  表示的是:前面的表达式出现了0次到1次,少是0次,多1次 ,另一个含义:阻止贪婪模式 [4][a-z]? "1231234ij"  

限定符:限定前面的表达式出现的次数
{} 更加的明确前面的表达式出现的次数 
{0,} 表示的是前面的表达式出现了0次到多次,和 *一样的 
{1,} 表示的是前面的表达式出现了1次到多次,和 +一样的 
{0,1} 表示的是前面的表达式出现了0次到1次,和 ?一样的 
{5,10} 表示的是前面的表达式出现了5次到10次 {4} 前面的表达式出现了4次

方括号用于查找某个范围内的字符:
 ^ 表示的是以什么开始,或者是取非(取反) ^[0-9] 以数字开头
 ^[a-z] 以小写字母开始 
 [^0-9] 取反,非数字 
 [^a-z] 非小写字母 
 [^0-9a-zA-Z_]
 
$ 表示的是以什么结束  
[0-9][a-z]$  必须以小写字母结束 
^[0-9][a-z]$ 相当于是严格模式   "3f2432e"  "4f"  

修正符\元字符
\d 数字中的任意一个, 
\D 非数字中的一个
\s 空白符中的一个
\S 非空白符
\w 非特殊符号 
\W 特殊符号
\b 匹配单词边界
\uxxxx  查找以十六进制xxxx规定的Unicode字符

修饰符
g 全局匹配
i 不区分大小写
m 多行匹配
x 忽略正则表达式中的空白
U 拒绝贪婪

.*   贪婪模式
.*?  非贪婪模式

反向引用
当我们匹配一个字符串的时候,可能会需要获取原字符串中的内容,此时我们可以这样做
//源字符串
2017-10-14
//正则
(\d{4})-(\d{2})-(\d{2})
//需要转换成 10/14/2017的格式,就用到反向表达式
$2/$3/$1
在正则中被分组的字符,我们可以通过$1 $2 $3...来捕获,如果需要引用原字符串的内容,只需要引用对应的 $1 $2 $3...即可。

创建正则表达式对象

//1.通过构造函数创建对象 
var reg=new RegExp(/\d{5}/); 
//字符串
var str="我的电话是10086"; 
//调用方法验证字符串是否匹配 
var flag=reg.test(str); console.log(flag);
//对象创建完毕--
var reg=new RegExp(/\d{5}/);
//调用方法验证字符串是否匹配 
var flag=reg.test("我的电话是10086");
console.log(flag);

//2.字面量的方式创建正则表达式对象
var reg=/\d{1,5}/;
var flag=reg.test("小苏的幸运数字:888"); 
console.log(flag);

身份证号 /^\d{17}(\d|[a-z])$/

手机号 /^\d{11}$/

qq号 /^\d{5,11}$/

固话 /^\d{3,4}[-]\d{7,8}$/

邮箱 /^\w+@\w+( \ .\w+){1,3}$/

日期 /\d{4}-\d{1,2}-\d{1,2}/

表单验证思路:

利用失去焦点事件onblur,(同时定义一个布尔值作为返回值,用作最后整体提交),判断如果值为空则提示输入值,如果有值则判断格式是否正确,格式错误返回false,格式正确返回true,最后在提交表单时整体验证只有所有都为true才可以提交。

正则表达式中的方法

search()

search() 用于检索字符串中指定的子字符串,或检索与正则表达式相匹配的字符串,并返回起始位置。

参数:正则或要查询的字符。

返回值:查询字符的起始位置。

使用正则搜索字符串,不区分大小写:

var str = "hello world";
var n = search(/ld/i);
console.log(n);

使用字符搜索字符串,不区分大小写:

var str = "hello world";
var n = search("ld");
console.log(n);

repalce()

repalce()用指定字符替换其他字符,或替换与正则匹配的字符。

参数:要修改的字符/匹配的正则,要替换的字符。

返回值:修改后的字符串。

var ss = "this is a good day";
var nn = ss.replace("good","bad");
//var nn = ss.replace(/good/i,"bad");
console.log(nn);//this is a bad day

test()

test()检测字符串是不是匹配某个模式,如果有匹配文本则返回true,否则返回false。

redExp.test();

var res = /e/.test("the best thing in the life are free");
console.log(res);//true

exec()

检索字符串中正则的匹配。

redExp.exec();

/e/.exec("this is the best thing , free");
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

代码搬运媛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值