JavaScript中函数、对象、类关系 记录

函数和对象的关系

函数可以有属性,对象也可以有属性,在函数名前使用 new 操作符即可返回一个函数的实例化对象

function fn () {}
fn.a = ‘haha‘
console.log(fn.a) //"haha"

let obj = {}
obj.a = ‘heihei‘
console.log(obj.a) //"heihei"

let newObj = new fn()

每个函数都有一个属性(prototype)原型对象,发现有constructor属性和 __poroto__属性,constructor指向创建它的构造器函数,这里要明确的是 函数也会有构造函数,而这个__poroto__ 与它的构造函数的 prototype 是同一个东西,见图 虚线指向的CFp就是构造函数的prototype,小的cf 是通过CF创建的对象实例

// fn.prototype
{
    constructor: ? doSomething(),
    __proto__: {
        constructor: ? Object(),
        hasOwnProperty: ? hasOwnProperty(),
        isPrototypeOf: ? isPrototypeOf(),
        propertyIsEnumerable: ? propertyIsEnumerable(),
        toLocaleString: ? toLocaleString(),
        toString: ? toString(),
        valueOf: ? valueOf()
    }
}

我们既可以在CF上添加属性,也可以在prototype 添加

区分:函数上添加属性,构造函数生成实例上添加、函数原型添加

function fn() {
    this.c = ‘c‘
}
fn.a = ‘a‘
fn.prototype.b = ‘b‘
let obj = new fn()
obj.d = ‘d‘

// fn.prototype 上定义 属性b ,obj定义了属性d,fn上添加了属性 a,
// fn函数内部有个this的属性c, 那么obj有几个属性呢? 答案是 c b d

下面就来分析下为什么会这样,首先图中的CF上的P1、P2在对象实例中均存在,参考下面的代码

function CF(){
    this.p1 = ‘p1‘,
    this.p2 = ‘p2‘
}

let cf1 = new CF()

见下图

可以看到属性均出现在了对象实例上,现在就来说一下构建实例的发生了什么

  1. 创建新对象cf1
  2. 构造函数的作用域赋给新对象,this指向新对象
  3. 执行构造函数中的代码,即注入属性
  4. 返回新对象

不过this上的属性构造函数是不具有的,而在函数上直接定义的属性它当然是有的,通过prototyoe设置的是在图的CFp位置的,实例继承它,也就是对象实例会得到这些属性,对象实例在添加prototype属性前创建依然有效

函数和对象的关系也就清楚了,通过构造函数方式可以创建对象,且可以得到函数原型上的属性和方法,也可以在构造函数里通过this为其设置一些不是公有的属性方法等

扩展:

  1. 使用this添加的属性具有一定“私有”性,但是还有些需要注意的地方,见下面代码

    function fn() {
      let value = ‘a‘
      this.a = ‘a‘
      this.privateValues = function (){
        return value
      }
    }
    let newObj1 = new fn()
    let newObj2 = new fn()
    console.log(newObj1.a === newObj2.a) //true,基本类型的比较只比较值
    console.log(newObj1.privateValues === newObj2.privateValues)
    // false,每次进入对象都不相同

    使用函数将其包裹起来返回具有更好的“私有”性

  2. 使用对象重写原型对象
    function fn() {}
    
    fn.prototype = {
      name: ‘kangkang‘,
      sayName: {
        console.log(this.name)
      }
    }

    使用对象重写会导致创建一个新的prototype对象, 对象里的 constructor不再指向构造函数,这时需要重新指定下

    function fn() {}
    
    fn.prototype = {
      constructor: fn
      name: ‘kangkang‘,
      sayName: {
        console.log(this.name)
      }
    }

继承

这里的继承想要有个较为深刻的理解,首先需要介绍下原型链:

每个实例对象(object )都有一个私有属性(称之为 proto)指向它的原型对象(prototype)。该原型对象也有一个自己的原型对象 ,层层向上直到一个对象的原型对象为 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节

参考MDN

由于每个实例都会继承原型链上的原型对象上的属性或方法,当你使用某个实例上所没有的属性时,会依着原型链一层一层寻找,那么继承这个概念也就很清楚了,我们将原型对象给需要继承的对象不就行了吗?其实之间会有些问题

function father() {}
function son() {}

father.prototype.sayHello = function() {
  console.log(‘hello‘)
}

son.prototype = father.prototype
let obj = new son()
obj.sayHello() // hello
//
father.prototype.sayHello = function() {
  console.log(‘haha‘)
}
 obj.sayHello() // haha
son.prototype.sayHello = function() {console.log(‘ddd‘)}
father.prototype.sayHello() //ddd 

上面这种“继承”是有问题的,不仅互相影响而且当father修改原型时会直接影响到son,进行改进

function father() {}
function son() {}

father.prototype.sayHello = function() {
    console.log(‘hello‘)
}

function wa() {  }

wa.prototype = father.prototype

son.prototype = new wa()
son.prototype.constructor = son
son.prototype.sayHello = function() {console.log(‘sss‘)}

father.prototype.sayHello() //hello

ECMAScript 5 中引入了一个新方法:Object.create(). 可以调用这个方法来创建一个新对象。新对象的原型就是调用 create 方法时传入的第一个参数

var a = {a: 1};
// a ---> Object.prototype ---> null

var b = Object.create(a);
// b ---> a ---> Object.prototype ---> null
console.log(b.a); // 1 (继承而来)

var c = Object.create(b);
// c ---> b ---> a ---> Object.prototype ---> null

var d = Object.create(null);
// d ---> null
console.log(d.hasOwnProperty);
 // undefined, 因为d没有继承Object.prototype

上面还是有点缺点,当 原型对象上有引用类型值得属性时

function a () {
  this.obj = {
    name: ‘kangkang‘,
    age: 3
  }
}
function b() {
  this.b = ‘b‘
}

b.prototype = new a()
var c = new b()
var d = new b()
c.obj.name=‘xixi‘
console.log(d.obj.name) //xixi

这时创建的实例在每次获得构造函数的引用值属性时,获得的值相同,值保存的是引用类型的地址,自然是同一个引用值了,所以互相影响,解决方法

看下面代码

function fn (name) {
    this.score = [90,98,99]
    this.name = name
}
function a () {
  fn.call(this,‘kangkang‘)
  this.age = 3
}
var c = new a()
var d = new a()

c.score[0] = 100
console.log(c.score) //[100, 98, 99]
console.log(d.score) //[90, 98, 99]

这种方法又缺少了原型上的继承,所以结合起来就是

function fn (name) {
    this.score = [90,98,99]
    this.name = name
}
fn.prototype.sayName = function() { console.log(this.name) }

function a () {
  fn.call(this,‘kangkang‘)
  this.age = 3
}
a.prototype = new fn()
a.prototype.constructor = a

var c = new a()
var d = new a()

c.score[0] = 100
console.log(c.score) //[100, 98, 99]
console.log(d.score) //[90, 98, 99]
console.log(c.sayName()) // kangkang

ES6 实现了 class,其中有 class constructor static extends super 这些关键字完整的实现“类”的功能, 它是语法糖,是基于上面所说的原型的概念实现的

  1. 定义类名 首字母大写
  2. constructor方法是一个特殊的方法,其用于创建和初始化使用class创建的一个对象,只能有一个
  3. 使用 super 关键字来调用一个父类的构造函数
  4. extends 关键字在类声明或类表达式中用于创建一个类作为另一个类的一个子类。
  5. static 关键字用来定义一个类的一个静态方法。调用静态方法不需要实例化该类,但不能通过一个类实例调用静态方法。静态方法通常用于为一个应用程序创建工具函数。
class Polygon {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}

class Square extends Polygon {
  constructor(sideLength) {
    super(sideLength, sideLength);
  }
  get area() {
    return this.height * this.width;
  }
  set sideLength(newLength) {
    this.height = newLength;
    this.width = newLength;
  }
}

var square = new Square(2);

static用法

class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }

    static distance(a, b) {
        const dx = a.x - b.x;
        const dy = a.y - b.y;

        return Math.hypot(dx, dy);
    }
}

const p1 = new Point(5, 5);
const p2 = new Point(10, 10);

console.log(Point.distance(p1, p2)); //7.0710678118654755
// 注意此处不是从实例中调用它的

拓展:

ES6属性简写和方法简写 参考阮一峰老师的博客

const o = {
  method() {
    return "Hello!";
  }
};
// 等同于

const o = {
  method: function() {
    return "Hello!";
  }
};

function f(x, y) {
  return {x, y};
}

// 等同于

function f(x, y) {
  return {x: x, y: y};
}

f(1, 2) // Object {x: 1, y: 2}

以上为查阅资料总结而得,如要详细准确还请自行查阅方能辨析有所得

原文地址:https://www.cnblogs.com/daixixi/p/9406604.html

时间: 2024-11-08 12:17:01

JavaScript中函数、对象、类关系 记录的相关文章

javascript中的对象继承关系

相信每个学习过其他语言的同学再去学习JavaScript时就会感觉到诸多的不适应, 这真是一个颠覆我们以前的编程思想的一门语言,先不要说它的各种数据类型以及表达 式的不同了,最让我们头疼,恐怕就是面向对象的部分了,在JavaScript中,是没有给定一 个创建对象的关键词的,它不像Java中一个class就可以创建一个对象,在JavaScript中, 对象是一个十分松散的的key-value对的组合,通常,我们在创建对象时,可以通过{}来直 接生成一个对象,就像我们之前所学的,对象中有属性,有行

JavaScript中函数对象的call()和apply()方法的总结

在JavaScript中,每个函数都有call()和apply()方法,用法举例如下: 先定义一个some_obj的对象,该对象有一个say()方法: var some_obj={ name:'Ninja', say:function(who){ return 'Haya ' + who + ', I am a '+ this.name; } }; 调用some_obj.say('Dude'); 输出:"Haya Dude, I am a Ninja" 再创建一个对象 var my_o

javascript中的对象继承关系(2)

在上一章中,我们着重介绍了JavaScript中类之间的原型继承关系:原型继承对于继承类中的方法来说是很方便的.那么我们今天就来看一下继承中的类继承以及类继承和原型继承的混用,所谓类继承,就是使用call或者apply方法来进行冒充继承: 1 function Desk(size,height){ 2 this.size=size; 3 this.height=height; 4 } 5 function MJDesk(size,height){ 6 Desk.call(this,size,he

JavaScript中函数对象和对象的区别

function Test (word) { console.log (word); } Test('哈哈,我是函数'); new Test('哈哈,我是对象'); //将以上的调用方式换种通俗易懂的方式 Test.call("哈哈,我是函数"); //相当于Test(); //相当于new Test(); var obj = {}; obj._proto_ = Test.prototype; Test.call(obj); 本质的区别就是,两次调用之中的this不同.调用Test('

3.2 STL中的函数对象类模板

*: STL中有一些函数对象类模板,如下所示: 1)例如要求两个double类型的x 和y 的积,可以: multiplies<double>()(x,y); 该表达式的值就是x*y的值. 2)less是STL中最常用的函数对象类模板,其定义如下: template<class _Tp> struct less { bool oprator()(const _Tp&_x,const _Tp&_y)const { return _c<_y; } } 要判断两个i

理解JavaScript中函数的使用

函数是进行模块化程序设计的基础,编写复杂的Ajax应用程序,必须对函数有更深入的了解. JavaScript中的函数不同于其他的语言,每个函数都是作为一个对象被维护和运行的.通过函数对象的性质,可以很方便的将一个函数赋值给一个变量或者将函数作为参数传递.在继续讲述之前,先看一下函数的使用语法: function func1(…){…} var func2=function(…){…}; var func3=function func4(…){…}; var func5=new Function(

STL 算法中函数对象和谓词

STL 算法中函数对象和谓词 函数对象和谓词定义 函数对象: 重载函数调用操作符的类,其对象常称为函数对象(function object),即它们是行为类似函数的对象.一个类对象,表现出一个函数的特征,就是通过"对象名+(参数列表)"的方式使用一个类对象,如果没有上下文,完全可以把它看作一个函数对待.          这是通过重载类的operator()来实现的.          "在标准库中,函数对象被广泛地使用以获得弹性",标准库中的很多算法都可以使用函数

(转)JavaScript中判断对象类型的种种方法

我们知道,JavaScript中检测对象类型的运算符有:typeof.instanceof,还有对象的constructor属性: 1) typeof 运算符 typeof 是一元运算符,返回结果是一个说明运算数类型的字符串.如:"number","string","boolean","object","function","undefined"(可用于判断变量是否存在). 但 type

JavaScript中函数的四种调用模式

理解函数的四种调用方法,可以有效的帮助我们分析和理解JavaScript代码.但是经常有人分不清楚或者不理解这四种调用模式,在JavaScript中,函数是一等公民,函数在JavaScript中是一个数据类型,而非像C#或其他描述性语言那样仅仅作为一个模块来使用.函数有四种调用模式,分别是:1.函数调用形式 2.方法调用形式 3.构造器调用形式 4.上下文调用形式(apply,call)这里所有的调用模式中,最主要的区别在于关键字 this 的意义.下面分别介绍这几种调用形式. 一.函数调用形式

私人定制javascript中函数小知识点

函数的定义 首先在javascript中,函数就是对象,程序可以随意操控它们.比如,可以给它们设置属性,甚至调用它们的方法.函数使用function关键字来定义.它既可以用在函数定义表达式,也可以用在函数声明语句中.函数声明function后面必须要更上函数名称也就是所谓的函数名称标识符.如果是函数表达式函数名称标识符可有可无.这段重点是函数是对象,所以函数表现出来的种种行为你想想成对象,那么很多疑惑可能就恍如昨日初见. 函数调用 4种方式来调用javascript函数: 1.作为函数 就是函数