js ECMAscript原型,继承,this,bind,闭包,浅/深拷贝,正则表达式
Posted 木心
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了js ECMAscript原型,继承,this,bind,闭包,浅/深拷贝,正则表达式相关的知识,希望对你有一定的参考价值。
目录:
1.原型的引入
代码一
function Person(name, age){ this.name = name; this.age = age; this.eat = function() { console.log("eat..."); }; } var per1 = new Person("小白", 10); var per2 = new Person("小黑", 10); console.log(per1.eat == per2.eat);//false
如何实现函数共享, 代码二:
function myEat(){ console.log("今天吃红烧土豆"); }; function Person(name, age){ this.name = name; this.age = age; this.eat = myEat; } var per1 = new Person("小白", 10); var per2 = new Person("小黑", 10); console.log(per1.eat == per2.eat);//true
但是代码二不好,因为外面可能定义变量var myEat = 10;造成命名冲突。
2.原型添加方法解决数据共享
代码:
function Person(name, age){ this.name = name; this.age = age; } //通过原型来添加方法 Person.prototype.eat = function() { console.log("eat..."); }; var per1 = new Person("zs1", 10); var per2 = new Person("zs2", 20); console.log(per1.eat == per2.eat);//true console.dir(per1); //通过浏览器查看,发现实例对象中没有eat()方法 console.dir(per2); console.dir(Person);
之前的写法
Student.prototype.height=""; Student.prototype.weight=""; Student.prototype.study=function(){}; Student.prototype.eat=function(){};
* 简单的原型语法
Student.prototype={ constructor:Student, height:"", weight:"", study:function(){}, eat:function(){} };
3.实例对象使用的属性和方法层层的搜索
实例对象使用的属性或方法,先在实例中查找,找到了直接使用,找不到,去对应的构造函数的原型对象prototype中查找,找不到,则报错。
4.为内置对象的原型对象中添加方法
我们能否为系统的内置对象的原型中添加方法(相当于改变源码)? 可以
String.prototype.myReverse=function(){ for(var i=this.length-1;i>=0;i--){ console.log(this[i]); } }; var str = "abcdefg"; str.myReverse();
5.原型及原型链
function Person(name, age){ this.name = name; this.age = age; } //通过原型来添加方法 Person.prototype.eat = function() { console.log("eat..."); }; var per1 = new Person("zs1", 10); var per2 = new Person("zs2", 20); console.log(per1.eat == per2.eat); // true console.log(per1.__proto__ == Person.prototype); // true
* 原型链: 是一种关系,实例对象和原型对象之间的关系,这个关系是通过原型(__proto__)来联系的
* 实例对象与原型对象是通过原型__proto__联系的,这个联系就是原型链
* 原型链最终的指向是Object的prototype中的__proto__是null
6.原型指向可以改变
代码1
function Student() {}; Student.prototype.study = function(){ console.log("我爱学习"); }; //Student的原型指向改变了,原型中原来的study()方法没有了 Student.prototype = { eat: function(){ console.log("我要吃饭"); } }; var stu = new Student(); console.dir(stu); stu.eat();//我要吃饭 //stu.study();//TypeError: stu.study is not a function
代码2
function Person() {}; Person.prototype.eat = function(){ console.log("我爱吃饭"); }; function Student() {}; Student.prototype.study = function(){ console.log("我爱学习"); }; //Student的原型指向改变了,指向了一个实例对象,原型中原来的study()方法没有了 Student.prototype = new Person(); var stu = new Student(); console.dir(stu);
结果一:Student.prototype = new Person();注释掉后
{} __proto__: {…} constructor: function Student() study: function study() __proto__: Object { … }
结果二:Student.prototype=new Person();没有注释
{} __proto__: {} __proto__: {…} constructor: function Person() eat: function eat() __proto__: Object { … }
7.原型指向改变如何添加原型方法
function Person(age) { this.age = age; }; Person.prototype.eat=function(){ console.log("我爱吃饭"); }; function Student(sex) { this.sex = sex; }; //Student的原型指向改变了,原型中原来的study()方法没有了 Student.prototype = new Person(10); //原型指向改变后,再添加方法 Student.prototype.study = function(){ console.log("我爱学习"); }; var stu = new Student("男"); stu.study(); console.dir(stu);
8.实例对象和原型对象属性重名问题
* 先在实例对象中找,找不到再到原型中找,再找不到,就返回undefined
* 如果是方法,如果实例对象和原型对象中都没有,报错TypeError: stu.XXXX is not a function
* 通过实例对象是否可以改变原型对象中的属性值,不能。
* 通过Student.prototype.sex="改变原型中sex的属性值";来改变原型的属性值
function Person(age,sex) { this.age=age; this.sex=sex; }; Person.prototype.eat=function(){ console.log("我爱吃饭"); }; function Student(sex) { this.sex=sex; }; //Student的原型指向改变了,原型中原来的study()方法没有了 Student.prototype=new Person(10,"女"); //原型指向改变后,再添加方法 Student.prototype.study=function(){ console.log("我爱学习"); }; var stu = new Student("男"); console.log(stu.sex); Student.prototype.sex="改变原型中sex的属性值"; console.dir(stu);
9.通过原型实现继承
* 面向对象的编程思想:根据需求,分析对象,找到对象有什么特征和行为,通过代码的方式来实现需求,
要想实现这个需求,就要创建对象,要想创建对象,就应该有构造函数,然后通过构造函数来创建对象,
通过对象调用属性和方法来实现相应的功能及需求。
* js不是一门面向对象的语言,js是一门基于对象的语言,那么为什么学习js还要学习面向对象。
- 因为面向对象的思想适合人的想法,编程起来会更加方便,以及后期的维护。
* 面向对象的编程语言中有类class的概念,但是js没有类的概念。但是js可以模拟面向对象的思想编程。
js通过构造函数来模拟类的概念。
* 面向对象的特性:封装、继承、多态
* 封装:就是包装
- 一个值存储在一个变量中--封装
- 一些重复代码放在一个函数中--封装
- 一系列的属性放在一个对象中--封装
- 一些功能类似的函数(方法)放在一个js文件中--封装
* 继承:首先继承是一种关系,类与类之间的关系。js中没有类,但是可以通过构造函数模拟类,
然后通过原型类实现继承。继承是为了数据共享,js中的继承也是为了数据共享。
* 原型作用一:实现数据共享,节省内存空间
原型作用二:实现继承
* 多态:一个对象有不同的行为
* js通过原型实现继承的代码:
function Person(name,age,sex) { this.name=name; this.age=age; this.sex=sex; }; Person.prototype.eat=function(){ console.log("我爱吃饭"); }; function Student(score) { this.score=score; }; //Student的原型指向改变了,原型中原来的study()方法没有了 Student.prototype=new Person("张三",20,"男"); //原型指向改变后,再添加方法 Student.prototype.study=function(){ console.log("我爱学习"); }; var stu = new Student(100); console.log(stu.name);//张三 console.log(stu.age);//20 console.log(stu.sex);//男 console.log(stu.score);//100 stu.eat();//我爱吃饭 stu.study();//我爱学习
10.借用构造函数实现继承
function Person(name,age,sex) { this.name=name; this.age=age; this.sex=sex; }; function Student(name,age,sex,score) { Person.call(this,name,age,sex);//借用构造函数实现继承 this.score=score; }; var stu = new Student("张三",20,"男",100); console.log(stu.name);//张三 console.log(stu.age);//20 console.log(stu.sex);//男 console.log(stu.score);//100
11.组合继承
function Person(name,age,sex) { this.name=name; this.age=age; this.sex=sex; }; Person.prototype.eat = function() { console.log("吃饭饭"); }; function Student(name,age,sex,score) { Person.call(this,name,age,sex);//借用构造函数实现继承 this.score=score; }; Student.prototype=new Person(); var stu = new Student("张三",20,"男",100); console.log(stu.name+"===="+stu.age+"===="+stu.sex+"===="+stu.score);//张三 stu.eat(); console.log(stu);
12.拷贝继承
* 拷贝继承:把一个对象中的属性或方法直接复制到另一个对象中。
* 代码一:
var obj1={ name="zs"; age:20, eat:function(){ console.log("吃土"); } }; var obj2=obj1;//改变obj2的指向 console.log(obj2.name,obj2.age); obj2.eat();
*代码二:
var obj1={ name="zs"; age:20, eat:function(){ console.log("吃土"); } }; var obj2={}; for(var key in obj1){ obj2[key]=obj1[key]; };
* 代码三:
function Person(){}; Person.prototype.name="zs"; Person.prototype.eat=function(){ console.log("吃饭"); }; var obj2={}; for(var key in Person.prototype){ obj2[key]=Person.prototype[key]; }
13.函数的角色及函数声明和函数表达式的区别
* 函数的角色:函数声明和函数表达式
* 函数声明
function f1(){
console.log("函数f1");
};
* 函数表达式
var f2=function(){
console.log("函数f2");
};
* 下面代码在IE8中输出"呵呵"。因为函数声明提前了,else里面的代码将前面的覆盖了。
- 火狐中还是输出"哈哈"
if(true){
function f1(){
console.log("哈哈");
};
}else{
function f1(){
console.log("呵呵");
};
}
f1();
* 以后尽量使用函数表达式
14.函数的this的指向问题
* 普通函数中的this
function f1(){
console.log(this);
};
window.f1();//函数调用,window可以省略不写,结果:this是window
* 定时器中的this
window.setTimeout(function( //window可以省略不写
console.log(this); //this是window
),1000);
* 构造函数或原型方法中的this----当前的实例对象
* 事件绑定方法的this-->指向绑定事件的对象
15.严格模式
"use strict";//严格模式 function f1(){ console.log(this); }; f1();//打印undefined window.f1();//打印window
16.函数是对象,对象不一定是函数
* 对象中有__proto__,函数中有prototype
* function f1(){};
console.dir(f1);//有prototype说明是函数,有__proto__说明又是对象
console.dir(Math);//有__proto__说明是对象,没有prototype说明不是函数
* 所有的函数实际上都是Func的构造函数创建出来的实例对象
var f1=new Function("num1","num2","return num1+num2");
console.log(f1(10,20));
等价于<==>
function f1(num1,num2){
return num1+num2
};
console.log(f1(10,20));
17.数组中函数的调用
// 函数的类型function function f1(){}; console.log(typeof f1);//类型是function // 数组可以储存任意类型 var arr=[ function(){ console.log(1); }, function(){ console.log(2); }, function(){ console.log(3); } ]; //数组中函数的调用 arr.forEach(function(ele){ ele(); });
-------------- 函数进阶apply、call、bind、闭包、沙箱、递归 --------------
1.apply()和call()方法
* 作用:可以改变this的指向
//"use strict";
function f1(x,y){
console.log("结果是"+(x+y)+"==="+this);
};
f1(10,20);//调用函数
* 上面代码的结果
结果是30===[object Window]
采用严格模式时,结果是30===undefined
* 代码一:
function f1(x,y){ console.log("结果是"+(x+y)+"==="+this); }; f1(10,20);//结果是30===[object Window] f1.apply(null,[10,20]);//结果是30===[object Window] f1.call(null,10,20);//结果是30===[object Window] f1.apply(this,[10,20]);//结果是30===[object Window] f1.call(this,10,20);//结果是30===[object Window] var obj={ name:"zs", age:20 }; f1.apply(obj,[10,20]);//结果是30===[object Object] f1.call(obj,10,20);//结果是30===[object Object]
* 代码二:
function Person(name,age){ this.name=name; this.age=age; }; Person.prototype.sayHi=function(){ console.log("hello,"+this.name); }; function Student(name){ this.name=name; }; var per=new Person("张三",20); per.sayHi();//hello,张三 var stu=new Student("李四"); per.sayHi.apply(stu);//hello,李四 per.sayHi.call(stu);//hello,李四
2.bind()方法
* bind(thisArg,参数)是复制一份,并且改变this的指向
function f1(x,y){ console.log("结果是"+(x+y)+"==="+this); }; var ff = f1.bind(null,10,20); ff();//结果是30===[object Window] var obj={ name:"zs", age:20 }; //先复制,后调用,复制时赋值 var fff=f1.bind(obj,10,20); fff();//结果是30===[object Object] //先复制,后调用,调用时赋值 var f2=f1.bind(obj); f2(20,30);//结果是50===[object Object]
3.函数中几个成员介绍
* 函数中有一个name属性--函数的名字,name属性是只读的,不能修改
* 函数中有一个arguments属性--实参的个数
* 函数中有一个length属性--形参的个数
* 函数中有一个caller属性--调用(f1函数在f2函数中调用)
* 代码:
function f1(x,y){ console.log(f1.name); console.log(f1.arguments.length); console.log(f1.length); console.log(fl.caller); } f1(10,20,30,40);//调用f1函数 funct f2(){ f1(1,2); }; f2();
4.函数作为参数
* 代码一:命名函数作为参数
function f1(fn){ console.log("函数f1"); fn(); } var f2=function (){ console.log("函数f2"); }; f1(f2);
* 代码二:匿名函数作为参数
function f1(fn){ console.log("函数f1"); fn(); } f1(function(){ console.log("匿名函数"); });
5.函数作为返回值
* 代码一:
function f1(){ return function(){ console.log("我是函数,是作为返回值使用"); }; } var ff = f1();//调用f1()函数,返回一个函数 ff();
* typeof和instanceof
var n=10; console.log(typeof n);//number console.log(typeof(n));//number var obj={}; console.log(typeof obj);//object console.log(typeof(obj));//object console.log(obj instanceof Object);//true function Person(){}; var per=new Person(); console.log(typeof per);//object console.log(typeof(per));//object console.log(per instanceof Object);//true console.log(per instanceof Person);//true
* Object.prototype.toString(对象) 获取对象的类型
console.log(Object.prototype.toString());//[object Object] console.log(Object.prototype.toString.call([]));//[object Array] console.log(Object.prototype.toString.call(Date));//[object Function] console.log(Object.prototype.toString.call(new Date()));//[object Date]
* Object.prototype.toString(对象)用来判断某对象的类型
function f1(a){ if(Object.prototype.toString.call(a)=="[object Array]"){ console.log("参数是一个数组"); } }; f1([]);//参数是一个数组
6.案例:电影排序
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body> <script type="text/javascript"> function File(name,size,time){ this.name=name; this.size=size; this.time=time; }; var f1= new File("jack.avi","400M","1997-12-10"); var f2= new File("xiaoy.avi","200M","2007-10-10"); var f3= new File("tom.avi","800M","1996-02-10"); arr=[f1,f2,f3]; function fn(attr){ return function getSort(obj1,obj2){ if(obj1[attr]>obj2[attr]){ return 1; }else if(obj1[attr]==obj2[attr]){ return 0; }else{ return -1; } }; }; var ff = fn(prompt("输入name或size或time")); arr.sort(ff); for(var i=0;i<arr.length;i++){ console.log(arr[i].name+"--"+arr[i].size+"--"+arr[i].time); } </script> </body> </html>
7.闭包
* 闭包的概念:函数A中,有一个函数B,函数B可以访问函数A定义的变量
* 闭包的模式:函数模式的闭包,对象模式的闭包
* 闭包的作用:缓存事件,延长作用域链
* 闭包的优缺点:优点:缓存数据 缺点:变量占用的内存没有及时释放
* 闭包的应用
* 函数模式的闭包
(function A(){
var num=10;
function B(){
console.log(num);
}
B();
})();
或另一种函数自调用写法
(function A(){
var num=10;
function B(){
console.log(num);
}
B();
}());
* 对象模式的闭包
function f3(){
var num=20;
return{
age:num
};
}
var obj=f3();
console.log(obj.age);
8.闭包的案例
function f1(){ var num = 10; return function (){ num++; return num; } } var ff = f1(); console.log(ff());//11 console.log(ff());//12 console.log(ff());//13
9.案例:闭包实现点赞
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body> <ul> <li style="list-style-type: none"><img src="uu.jpg" style="width: 300px"><br/> <input type="button" value="点赞(0)" style="font-size: 25px"></li> <li style="list-style-type: none"><img src="uu.jpg" style="width: 300px"><br/> <input type="button" value="点赞(0)" style="font-size: 25px"></li> </ul> <script type="text/javascript"> var eles = document.getElementsByTagName("input"); function getValue(){ var value=0; return function (){ value++; this.value="点赞("+value+")"; }; }; for(var i=0;i<eles.length;i++){ eles[i].onclick=getValue(); } </script> </body> </html>
10.沙箱
* 沙箱:模拟小环境
var num=10;
(function(){
var num=20;
console.log(num);
}());
11.递归
-------------- 内置的方法(深拷贝、浅拷贝、遍历DOM树)、正则表达式 --------------
1.浅拷贝
var obj1={
name:"张三",
age:10
};
var obj2={};
function extend(a,b){
for(var key in a){
b[key]=a[key];
}
}
extend(obj1,obj2);
console.dir(obj2);
2.深拷贝
var obj1={
name:"张三",
age:10,
car:["宝马","奔驰"],
dog:{
name:"大黄",
age:5,
cat:{
name:"阿才",
age:2,
color:["yellow","white"]
}
}
};
var obj2={};
function extend(a,b){
for(var key in a){
var item=a[key];
if(item instanceof Array){
console.log("复制数组");
b[key]=[];
extend(item,b[key]);
}else if(item instanceof Object){
console.log("复制对象");
b[key]={};
extend(item,b[key]);
}else{
console.log("复制属性");
b[key]=item;
}
}
}
extend(obj1,obj2);
console.dir(obj1);
console.dir(obj2);
3.遍历DOM树
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body> <div> <ul> <li><a href=""><img src="uu-small.jpg"></a></li> <li><a href=""><img src="uu-small.jpg"></a></li> </ul> </div> <div><a href="">百度</a></div> <p><a href="">谷歌</a></p> <script type="text/javascript"> var htmlObj=document.getElementsByTagName("html")[0]; function fn(obj){ for(var i=0;i<obj.children.length;i++){ console.log(obj.children[i].localName);//输出标签名字 if(obj.children[i].children){ fn(obj.children[i]); } } }; fn(htmlObj); </script> </body> </html>
4.正则表达式
以上是关于js ECMAscript原型,继承,this,bind,闭包,浅/深拷贝,正则表达式的主要内容,如果未能解决你的问题,请参考以下文章