面向对象的类和实例对应原型链,普通函数和构造函数,函数方法的共用,原型的设置初始化值

三大特点:数据封装、继承和多态

继承:

JavaScript不区分类和实例的概念,而是通过原型(prototype)来实现面向对象编程。

JavaScript的原型链和Java的Class区别就在,它没有“Class”的概念,所有对象都是实例,所谓继承关系不过是把一个对象的原型指向另一个对象而已。

前面所讲,JavaScript对象包括了数组Array,函数function,{}对象。JavaScript对每个创建的对象都会设置一个原型,指向它的原型对象。在原型对象中定义的方法,对于每个对象是通用的。

例如,创建一个Array对象:

var arr = [1, 2, 3];
原型链如下:
arr ----> Array.prototype ----> Object.prototype ----> null
typeof(arr);  // "object"
typeof(Array.prototype);  // "object"
typeof(Array);  // function
Array.prototype对象定义了indexOf()、shift()等方法,因此你可以在所有的Array对象上直接调用这些方法。
由于数组的类型是object,如何确定是数组,有以下方式:
Array.isArray(arr);     // 返回 true
arr instanceof Array     // 返回 true
arr.constructor.toString().indexOf("Array") > -1;   // 返回true

  创建一个函数时:

function foo() {
    return 0;}
原型链是:
foo ----> Function.prototype ----> Object.prototype ----> null
typeof(foo);  // "function"
typeof(Function.prototype);  // "function"
typeof(Function);  // "function"
由于Function.prototype定义了apply()等方法,因此,所有函数都可以调用apply()方法。

如果原型链很长,那么访问一个对象的属性就会因为花更多的时间查找而变得更慢,因此要注意不要把原型链搞得太长。

构造函数:

首先理清之前函数定义的方法:

  • 申明式函数定义:function  函数名 (参数){语句块}      没有分号结尾。这种定义方式,会将函数声明提升到该函数所在作用域的最开头,也是就无论你在哪儿使用这种方式声明的函数,在这个作用域内,你都可以调用这个函数为你所用。
  • 函数表达式:let fun = function(){};   有分号结尾。此方式定义的函数,作用域为该定义之后。
  • new Function 形式: var fun1 = new Function (arg1 , arg2 ,arg3 ,…, argN , body  );有分号结尾。函数的内置对象Function。Function构造函数所有的参数都是字符串类型。除了最后一个参数, 其余的参数都作为生成函数的参数即形参。这里可以没有参数。最后一个参数, 表示的是要创建函数的函数体。这种效率很慢。
var temp=500
function foo1()
{
     var temp = 100;
     console.log('foo1 this.temp=',this.temp,this)
     return temp + this.temp;
}

var foo3 = function() 
{
     var temp = 100;
     console.log('foo3 this.temp=',this.temp,this)
     return temp + this.temp;
}

var foo2 = new Function('var temp = 100; return temp + this.temp;');

console.log('foo1 type=',typeof(foo1));  
// function,并不会运行里面的程序
console.log(foo1());   
/* 
foo1 this.temp= 500 Window https://www.liaoxuefeng.com/wiki/1022910821149312/1023021997355072
600
*/
console.log('foo2 type=',typeof(foo2));  // function
console.log(foo2());   // 600
console.log('foo3 type=',typeof(foo3));  // function
console.log(foo3());   
/*
foo3 this.temp= 500 Window https://www.liaoxuefeng.com/wiki/1022910821149312/1023021997355072
600
*/

区别:

  • 构造函数也是一个普通函数,创建方式和普通函数一样,但普通函数首字母小写,构造函数习惯上首字母大写.
  • 作用不一样(构造函数用来新建实例对象)
  • 调用方式不一样。普通函数的调用方式:直接调用 person(); 构造函数的调用方式:需要使用new关键字来调用 new Person();
  • 构造函数的函数名与类名相同:Person( ) 这个构造函数,Person 既是函数名,也是这个对象的名。
  • 内部用this 来构造属性和方法它绑定的this指向新创建的对象,并默认返回this,也就是说,不需要在最后写return this。
function Person(name,job,age)  
{
     this.name=name;
     this.job=job;
     this.age=age;
     this.sayHi=function()
         {
          return `Hi,${this.name}!`;  //注意这里使用反引号
         }; 
// 不需要return
 } 
 
var xiaoming = new Person('小明');  
/*
 如果不写new,Person就是一个普通函数,它返回undefined,那么变量xiaoming的值就是undefined。
但是,如果写了new,Person就变成了一个构造函数。Person.prototype类似一个类对象,xiaoming类似一个实例对象。
*/
console.log(typeof(Person),"Person:",Person);
// function Person: function Person(name, job, age)
console.log(typeof(xiaoming),"xiaoming:",xiaoming);
// object xiaoming: Object { name: "小明", job: undefined, age: undefined, sayHi: sayHi() }

那么,xiaoming就是一个对象,Person.prototype也是一个对象,其原型链如下:

xiaoming ----> Person.prototype ----> Object.prototype ----> null
typeof(Person.prototype); // "object"
typeof(Person);  // "function"

也就是说,xiaoming的原型指向函数Person的原型。如果你又创建了xiaohongxiaojun,那么这些对象的原型与xiaoming是一样的,原型链如下:

xiaoming ↘
xiaohong -→ Person.prototype ----> Object.prototype ----> null
xiaojun  ↗

new Student()创建的对象还从原型上获得了一个constructor属性,它指向函数Student本身:

xiaoming.constructor === Student.prototype.constructor; // true
Student.prototype.constructor === Student; // true

Object.getPrototypeOf(xiaoming) === Student.prototype; // true

xiaoming instanceof Student; // true

实例共享一个函数方法:

将各个实例需要的公用的函数放到原型中去,那么这些实例对象实际上只需要共享同一个函数,这样就可以节省内存

xiaoming=new Person('小明');
xiaohong=new Person('小红');

xiaoming.name; // '小明'
xiaohong.name; // '小红'
xiaoming.sayHi; // function sayHi()
xiaohong.sayHi; // function sayHi()
xiaoming.sayHi === xiaohong.sayHi; // false  说明同一方法在不同对象中分别创建了

公用同一方法,节省内存开销
function Person(name,job,age)  
{
     this.name=name;
     this.job=job;
     this.age=age;
     /*this.sayHi=function()
         {
          return `Hi,${this.name}!`
         };  公用方法不在Person函数里面定义,而是在Person.prototype这个原型对象中定义(或者说添加)该方法
      */ 
// 不需要return
 } 
Person.prototype.sayHi=function(){return `Hi`;};
 
var xiaoming = new Person('小明'), xiaohong = new Person('小红'); 

xiaoming.sayHi;  // function sayHi()
xiaohong.sayHi;  // function sayHi()
xiaohong.sayHi===xiaoming.sayHi;  // true  共用函数
xiaohong.sayHi();  // Hi,小红!
xiaohong.sayHi();  // Hi,小明!

初始化原型对象的一个范例:

// 定义构造函数,使用||来选择默认的初始化值,传入的参数是一个对象
function Student(props) {
    this.name = props.name || '匿名'; // 默认值为'匿名'
    this.grade = props.grade || 1; // 默认值为1
    console.log('this=',this);
}
// 使函数共用,减少内存的开销
Student.prototype.hello = function () {
    alert('Hello, ' + this.name + '!');
};
//  定义一个createStudent()函数,显然:一是不需要new来调用,二是参数非常灵活,可以不传,也可以传少数的参数
function createStudent(props) {
    return new Student(props || {})
}

var xiaoming = createStudent({
    name: '小明'
});
// 创建实例的时候就会得到this= Student { name: '小明', grade: 1 }

xiaoming.grade; // 1

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值