如上例所示,对sayHello函数的所有引用(包括person1,Person.prototype和helloFunction)都引用同一函数.
在调用函数的过程中,其值取决于我们如何调用函数. 在正常情况下,我们通过表达式person1.sayHello()调用函数: 即从对象的属性中获取被调用的函数. 这时,将其设置为我们获得的功能的对象(即person1). 这就是为什么person1.sayHello()使用名称“ Alice”而person2.sayHello()使用名称“ bob”的原因.

但是,当我们使用不同的调用方法时,此方法的值也不同. 从变量helloFunction()调用时,将其设置为全局对象(即浏览器中的窗口). 由于对象(很有可能)没有firstName属性,因此我们得到的结果是“你好,我不确定”. (这是松散模式下的结果,结果会有所不同(这时会产生错误). 但是为了避免混淆,在此不再赘述). 另外,我们可以使用Function#call(或Function#apply)显式设置this的值,如上例末所示.
有关更多信息,请参阅和
创建一个或多个类的特殊版本的方法称为继承(Javascript仅支持单一继承). 创建的类的特殊版本通常称为子类,其他类通常称为父类. 在Javascript中,继承是通过为子类提供父类的实例并专门化子类来实现的. 在现代浏览器中,您可以使用实现继承.
JavaScript无法检测到子类的prototype.constructor(请参见),因此我们必须手动对其进行声明.
在下面的示例中,我们将Student类定义为Person类的子类. 之后,我们重新定义sayHello()方法并添加sayGoodBye()方法.
// 定义Person构造器 function Person(firstName) { this.firstName = firstName; } // 在Person.prototype中加入方法 Person.prototype.walk = function(){ alert("I am walking!"); }; Person.prototype.sayHello = function(){ alert("Hello, I'm " + this.firstName); }; // 定义Student构造器 function Student(firstName, subject) { // 调用父类构造器, 确保(使用Function#call)"this" 在调用过程中设置正确 Person.call(this, firstName); // 初始化Student类特有属性 this.subject = subject; }; // 建立一个由Person.prototype继承而来的Student.prototype对象. // 注意: 常见的错误是使用 "new Person()"来建立Student.prototype. // 这样做的错误之处有很多, 最重要的一点是我们在实例化时 // 不能赋予Person类任何的FirstName参数 // 调用Person的正确位置如下,我们从Student中来调用它 Student.prototype = Object.create(Person.prototype); // See note below // 设置"constructor" 属性指向Student Student.prototype.constructor = Student; // 更换"sayHello" 方法 Student.prototype.sayHello = function(){ console.log("Hello, I'm " + this.firstName + ". I'm studying " + this.subject + "."); }; // 加入"sayGoodBye" 方法 Student.prototype.sayGoodBye = function(){ console.log("Goodbye!"); }; // 测试实例: var student1 = new Student("Janet", "Applied Physics"); student1.sayHello(); // "Hello, I'm Janet. I'm studying Applied Physics." student1.walk(); // "I am walking!" student1.sayGoodBye(); // "Goodbye!" // Check that instanceof works correctly console.log(student1 instanceof Person); // true console.log(student1 instanceof Student); // true
对于“ Student.prototype = Object.create(Person.prototype);”行,在不支持方法的旧JavaScript引擎中,可以使用“ polyfill”(也称为“ shim”),请参见文章链接),或使用函数来获取相同的返回值,如下所示:
function createObject(proto) {
function ctor() { }
ctor.prototype = proto;
return new ctor();
}
// Usage:
Student.prototype = createObject(Person.prototype);
有关更多信息,请参考链接,其中还有旧JavaScript引擎的兼容方案(填充程序).
在前面的示例中,尽管Student类不需要知道Person类的walk()方法是如何实现的,但是它仍然可以使用此方法. 除非我们想要更改它,否则Student类不需要显式定义此方法. 这称为封装. 对于从父类继承的所有方法,您只需要在子类中定义要更改的方法即可.
抽象是一种机制,可以模拟工作问题的常见部分. 这可以通过继承(修饰)或组合来实现.
JavaScript通过继承实现统一化,并通过将类的实例作为其他对象的属性值来实现合成.
JavaScript Function类是从Object类继承的(这是典型的说明). Function.prototype的属性是一个Object实例(这是一种典型的组合).
var foo = function(){};
console.log( 'foo is a Function: ' + (foo instanceof Function) ); // logs "foo is a Function: true"
console.log( 'foo.prototype is an Object: ' + (foo.prototype instanceof Object) ); // logs "foo.prototype is an Object: true"
与原型属性中定义的所有方法和属性一样,不同的类可以使用相同的名称来定义方法;方法对它们所在的类起作用. 只有当两个类都不具有父子关系时(在继承链中,一个类不会从其他类继承),这才成立.
本文显示的面向对象编程技术不是实现它的唯一方法. JavaScript中的面向对象的实现非常灵活.
类似地,本文中显示的技术没有使用任何语言技巧,也没有模仿其他语言中对象理论的实现.
JavaScript中还有其他更高级的面向对象技术,但这不在本文讨论范围之内.
维基百科. 面向对象程序设计,Wikipedia. “封装(面向对象的程序设计)”
本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/jisuanjixue/article-286113-2.html
他有颜而不止于此