函数对象
在javascript中,函数也可以被称之 "函数对象".我们可以为函数对象设置属性,赋值等操作.
//定义一个函数对象
function test(){}
//为函数对象设置任意属性,这里我们设置description(描述),用于描述该函数的作用
test.description = "这是一个测试函数"
//获取函数对象的描述属性
console.log(test.description);
//函数依然可以正常调用
test();
Prototype
在javascript编程中,所有的函数都有一个 prototype属性,该属性指向的是一个对象.我们称之为 原型对象.如下图
注意: 虽然每个函数都会有自己的原型对象,但是本课程中,我们只考虑构造函数的原型对象.
原型对象的作用
- 共享属性和方法
- 通过构造函数创建的实例对象,默认拥有该构造函数的原型对象的所有属性和方法
//定义一个构造函数
function Person(name,age){
this.name = name;
this.age = age;
}
//定义一个普通对象
var obj = {
country:"中国",
sayHi: function(){
console.log("您好");
}
}
//将Person构造函数的prototype属性指向obj,也就是将obj设置为Person构造函数的原型对象
Person.prototype = obj;
//创建Person的实例
var p1 = new Person("zmt",33);
//访问Person实例对象自带的属性
console.log(p1.name);
console.log(p1.age);
//访问原型对象的属性
console.log(p1.country);
p1.sayHi();
从上面的例子中,我们可以发现 当我们将Person构造函数的prototype属性指向obj对象的时候,通过Person构造函数创建出来的对象就默认拥有了obj对象的属性和方法了
多个构造函数的原型指向同一个对象
function Worker(){}
function Student(){}
var info = {
country;"中国",
play:function(game){
console.log("玩"+game);
}
}
Worker.prototype = info;
Student.prototype = info;
var w1 = new Worker();
w1.play("LOL");
console.log(w1.country);
var s1 = new Student();
s1.play("王者");
console.log(s1.country);
__proto__
每个非null实例对象,都默认拥有一个__proto__属性,该属性指向实例对象的构造函数的原型对象.
function Person(){}
var obj = {
country:"中国"
}
Person.prototype = obj;
var p1 = new Person();
console.log(p1.__proto__);
console.log(Person);
console.log(p1.__proto__ === Person.prototype); //true
原型链
由于所有非null对象,都会有一个proto属性,当我们访问某个属性或者方法,如果在当前对象中没有该属性或方法,则会一直通过proto找到原型对象,如果原型对象也没有该属性和方法,则继续沿着原型对象的proto属性往上以及原型对象找,直到找到属性或者最终的对象为null时结束.在这个过程中,通过proto形成的线路,我们就称之为原型链.如下图
代码示例
function Person(name){
this.name = name;
}
var obj = {
country: "中国"
}
//将obj对象设置为Person构造函数的原型对象
Person.prototype = obj;
//创建Person实例
var p1 = new Person("zmt");
//访问p1对象的name属性
console.log(p1.name);
//访问原型对象的country属性
console.log(p1.country);
//访问一个p1对象和obj对象都不存在属性,得到结果为null
console.log(p1.cityname);
//查看obj的构造函数,得到结果是 ƒ Object() { [native code] }
console.log(obj.constructor);
//为obj的构造函数的原型对象新增一个cityname属性
obj.constructor.prototype.cityname = "深圳";
//再通过p1访问cityname
console.log(p1.cityname);
/*
根据原型链获取各自的原型对象
*/
console.log(p1.__proto__);//obj对象
console.log(p1.__proto__.__proto__);//Object对象
console.log(p1.__proto__.__proto__.__proto__);//最终结果为null
原型链在开发中的实际应用
我们想要为数组新增一些功能,但是我们不能直接去修改JS源码,因此,可以通过网Array的构造函数的原型对象上新增函数的方式,为js数组进行扩展
数组实现交集
var front = ["html","css","js"]; var back = ["java","mysql","spring","js"]; Array.prototype.intersection = function(target){ var _this = this; var rs = this.filter(function(item){ return target.indexOf(item)>-1; }) return rs } var result = front.intersection(back); console.log(result);
- 实现并集
var front = ["html","css","js"]; var back = ["java","mysql","spring","js"]; Array.prototype.union = function(target){ var _this = this; //过滤target数组中的元素 var rs = target.filter(function(item){ return _this.indexOf(item)===-1 }) return this.concat(rs);; } //直接通过front数组调用union函数 var result = front.union(back);
数组去重
var arr1 = [1,2,4,3,4,1,2,3,5]; //为数组构造函数的原型对象添加一个unique方法 Array.prototype.unique = function(){ /* 用于存储已遍历过的元素,例如 { 1: true, 2: true } */ var rs = {} //将没有重复元素放到这个数组里 var arr = [] //this表示调用unique函数的数组 this.forEach(function(item){ if(!rs[item]){ rs[item] = true; arr.push(item); } }) return arr; } var result = arr1.unique();
有时候我们要用到别人已经封装好的对象,但是功能不够,这时候我们虽然可以直接查看和修改该对象的源码.但是我们奉着不动别人代码一分一毫的原则,我们只能在该对象的构造函数的原型对象上动手脚了,代码如下
//假设这是你导入的别人封装好的对象,里面有很多方法,你不能直接在里面添加新的代码
var mtobj = {
test:function(){
console.log("测试");
},
print:function(){
console.log("打印");
}
}
//为mtobj对象的构造函数的原型对象添加新的函数
mtobj.constructor.prototype.sum = function(arr){
var rs = 0;
for(var i=0;i<=arr.length-1;i++){
rs += arr[i];
}
return rs;
}
mtobj.__proto__.odd = function(arr){
var rs = arr.filter(function(item){
return item%2==1
})
return rs;
}
var result = mtobj.sum([1,2,3,4,5,6]);
console.log(result);
var result2 = mtobj.odd([1,2,3,4,5,6]);
console.log(result2);