精通javascript:面向对象的js

javascript 引用

reference是一个指向对象实际位置的指针。The premise is that a physical
object is never a reference. A string is always a string; an array is always an array. However,
multiple variables can refer to that same object. It is this system of references that JavaScript
is based around. By maintaining sets of references to other objects, the language affords you
much more flexibility.
但有一个前提,实际的对象肯定不会是引用。字符串永远是字符串,数组永远是数组。不过多个变量却能够指向同一对象。js基于的就是这样的一个引用系统。
 此外,对象可以包含一系列属性(property),这些属性也不过是到其他对象的(如字符串,数组等)的引用。如果多个变量指向的是同一个对象,那该对象的类型一改变,所有的这些变量也会跟着相应改变。
/
/ Set obj to an empty object
var obj = new Object();

// objRef now refers to the other object
var objRef = obj;

// Modify a property in the original object
obj.oneProperty = true;

// We now see that that change is represented in both variables
// (Since they both refer to the same object)
alert( obj.oneProperty == objRef.oneProperty );
 
注意obj和objRef都是引用(引用的是同一个对象),一旦修改源对象的属性,所有的都修改了。
// Create an array of items
var items = new Array( "one", "two", "three" );

// Create a reference to the array of items
var itemsRef = items;

// Add an item to the original array
items.push( "four" );

// The length of each array should be the same,
// since they both point to the same array object
alert( items.length == itemsRef.length );
 
必须记住的是:引用指向的只能是具体的对象,而不是另一个引用不行perl语言允许多层引用。js里的结果是沿着引用链一直上溯到原来的那个对象。
下面的代码表示了:实际对象已经改变了,但原来指向他的引用任然保持旧的对象。
// Set items to an array (object) of strings
var items = new Array( "one", "two", "three" );

// Set itemsRef to a reference to items
var itemsRef = items;

// Set items to equal a new object
items = new Array( "new", "array" );

// items and itemsRef now point to different objects.
// items points to new Array( "new", "array" )
// itemsRef points to new Array( "one", "two", "three" )
alert( items != itemsRef );
最后,来看特殊的例子,看似是自修改对象,其结果却产生了一个新的非引用对象,在执行字符串连接操作时,结果总会是一个新的字符串对象,而非原来的字符串的修改版本。
 
// Set item equal to a new string object
var item = "test";

// itemRef now refers to the same string object
var itemRef = item;

// Concatenate some new text onto the string object
// NOTE: This creates a new object, and does not modify
// the original object.
item += "ing"; 创建了一个新的对象,不是修改

// The values of item and itemRef are NOT equal, as a whole
// new string object has been created
alert( item != itemRef );
js scope
scope在js很特殊,是由函数来划分的,而不是由block来的,这样导致的结果是某些代码不好理解。
 
当变量缺乏声明是,如果变量没有明显定义,他就是在全局定义的,虽然他可能只在这个函数作用域的范围内使用
function test(){
     foo="test";
}
test();
document.write(window.foo=="test");
我们发现foo现在是在全局作用域下。
 
闭包
closure意味着内层的函数可以引用存在于包围他的函数内的变量,即使外层函数的执行已经终止
 
闭包还能解决一个常见的js编写问题,js新手大多会留下许多全局变量,这是一个坏习惯,因为这些多余的变量可能会悄悄的影响其他的库。
// Create a new anonymous function, to use as a wrapper
(function(){
    // The variable that would, normally, be global
    var msg = "Thanks for visiting!";

    // Binding a new function to a global object
    window.onunload = function(){
        // Which uses the 'hidden' variable
        alert( msg );
    };

// Close off the anonymous function and execute it
}());
闭包使用的一个问题,闭包允许你引用父函数中的变量,但提供的值并非该变量创建时的值,而是在父函数范围内的最终值,你会看到,这样带来的最常见的问题是在for循环中,有一个变量作为循环计数(如i),在这个循环里创建了新的函数,利用闭包来引用循环的计数器。问题是:在这个新的闭包被调用时,它引用的计数器值使其最后一次的赋值(比如数组的最后一个位置),
而不是你期望的那个值。如下所示用了一个匿名函数来激发作用域的例子,用实例证明了 上面所期望的闭包效果是可以达到的。
var obj=document.getElementById("main");
var items=["click","keypress"];
for(var i=0;i<items.length;i++)
{
     var item=items[i];
     obj['on'+item]=function(){
          alert("thanks for your"+item);
     };
}
 
不管是click还是keypress,都是输出的是keypress,我们换成click和dblclick更明显,当click时,输出的依然是thanks for your
dbclick。(错误i最后变成了3
解决办法:使用一个自执行的匿名函数来激发作用域
// An element with an ID of main
var obj = document.getElementById("main");

// An array of items to bind to
var items = [ "click", "keypress" ];

// Iterate through each of the items
for ( var i = 0; i < items.length; i++ ) {
    // Use a self-executed anonymous function to induce scope
    (function(){
        // Bind a function to the elment
        obj[ "on" + items[i] ] = function() {
            // items[i] refers to a parent variable that has been successfully
            // scoped within the context of this for loop
            alert( "Thanks for your " + items[i] );
        };
    })();
}
click后输出click,keypress输出keypress。
个人心得:
js为什么函数里面可以包含函数(一般的语言 是不行的)。大概是因为js里面没有类这个概念,我们可以通过函数来模拟类,而高级语言里类可以有嵌套类,这就解释了为什么js里面函数可以包含函数。
 
 
js context上下文对象
 在js中,你的代码总有一个上下文对象(代码处在该对象内)。这是oo的常见特点,但其他语言没有js发挥的那么极致。
 上下文对象是通过this变量体现的。这个变量永远指向当前代码所处的对象中。全局对象其实就是window对象的属性。这意味着即使是在全局上下文中,this变量也能指向一个对象。上下文对象可以称为一个强大的工具。
 
var obj = {
    yes: function(){
        // this == obj
        this.val = true;
    },
    no: function(){
        this.val = false;
    }
};

// We see that there is no val property in the 'obj' object
alert( obj.val == null );

// We run the yes function and it changes the val property
// associated with the 'obj' object

obj.yes();
alert( obj.val == true );

// However, we now point window.no to the obj.no method and run it 把window.no指向obj.no并执行之
window.no = obj.no;
window.no();

// This results in the obj object staying the same (as the context was
// switched to the window object)
alert( obj.val == true );

// and window val property getting updated.
alert( window.val == false );
 
我们把obj.no变量的上下文对象切换为window变量时,代码变得不好理解了,幸运的是,js提供了一套方法更好的让我们实现和理解:
下面展示了call和apply2个方法,可以用于实现这一功能:
// A simple that sets the color style of its context
function changeColor( color ) {
    this.style.color = color;
}

// Calling it on the window object, which fails, since it doesn't
// have a style object
changeColor( "white" ); 在window对象中调用此函数会失败,因为widnow没有style属性。

// Find the element with an ID of main
var main = document.getElementById("main");

// Set its color to black, using the call method
// The call method sets the context with the first argument
// and passes all the other arguments as arguments to the function
changeColor.call( main, "black" ); 、、main颜色会是black

// A function that sets the color on  the body element
function setBodyColor() {
    // The apply method sets the context to the body element
    // with the first argument, the second argument is an array
    // of arguments that gets passed to the function
    changeColor.apply( document.body, arguments );
}

// Set the background color of the body to black
setBodyColor( "black" );
上下文对象在面向对象就更明显了。
 
 
js面向对象基础
 面向对象的js这个词有点多余,因为js这门语言就是完全面向对象的,也不可能以非面对对象的方法来使用,不过大多数人的常见弱点就是在于按照功能编写代码,而不考虑上下文或者组织。
 
对象。
  事实上,这门语言的所有东西都是对象。

// Creates a new Object object and stores it in 'obj'
var obj = new Object();

// Set some properties of the object to different values
obj.val = 5;
obj.click = function(){
alert( "hello" );
};

// Here is some equivalent code, using the {…} shorthand
// along with key-value pairs for defining properties
var obj = {

// Set the property names and values use key/value pairs
val: 5,
click: function(){
alert( "hello" );
}

};

对象的创建

 js没有类的概念,

In JavaScript, objects can create
new objects, and objects can inherit from other objects. This whole concept is called
prototypal inheritance and will be discussed more later in the “Public Methods” section

。js对象本身可以创建新对象,而对象也可以继承自其它对象。这个概念叫“原型化继承)。

Fundamentally, though, there still needs to be a way to create a new object, no matter
what type of object scheme JavaScript uses. JavaScript makes it so that any function can also
be instantiated as an object. In reality, it sounds a lot more confusing than it is. It’s a lot like
having a piece of dough (which is a raw object) that is molded using a cookie cutter (which
is an object constructor, using an object’s prototype

// A simple function which takes a name and saves
// it to the current context
function User( name ) {
this.name = name;
}

// Create a new instance of that function, with the specified name
var me = new User( "My Name" );

// We can see that it's name has been set as a property of itself
alert( me.name == "My Name" );

// And that it is an instance of the User object
alert( me.constructor == User );

// Now, since User() is just a function, what happens
// when we treat it as such? 如果我们把它作为函数
User( "Test" );

// Since it's 'this' context wasn't set, it defaults to the global 'window'
// object, meaning that window.name is equal to the name provided
alert( window.name == "Test" );

为this上下文对象没有指定,所以默认为全局的window对象

上面展示了constructor属性的使用,这一属性在每个对象中都存在,并一直指向创建它的函数。这样一来就可以有效地复制对象了,

用同一个基类创建对象并赋予不同的属性。

// Create a new, simple, User object
function User() {}

// Create a new User object
var me = new User();

// Also creates a new User object (from the
// constructor reference of the first)
var you = new me.constructor();

// We can see that the constructors are, in fact, the same
alert( me.constructor == you.constructor );

现在我们知道如何创建简单的对象了,是时候添加一些让对象更有用的东西了---上下文相关方法contextual method和属性。

1.公共方法

Public methods are completely accessible by the end user within the context of the object. To
achieve these public methods, which are available on every instance of a particular object, you
need to learn about a property called prototype, which simply contains an object that will act
as a base reference for all new copies of its parent object. Essentially, any property of the prototype
will be available on every instance of that object. This creation/reference process gives

prototype包含了一个对象,该对象可以作为所有新副本的基引用(base reference)。本质来说,所有对象的元凶的属性都能在

该对象的每个实例中找到
us a cheap version of inheritance, which I discuss in Chapter 3.
Since an object prototype is just an object, you can attach new properties to them, just
like any other object. Attaching new properties to a prototype will make them a part of every
object instantiated from the original prototype, effectively making all the properties public
(and accessible by all). Listing 2-22 shows an example of this

因为对象的原型也是对象,和其他任何对象一样,也可以添加新属性,给原型添加新属性的结果是由该原型实例化的每个对象都会获得这些属性,也就使这些属性公有化了(能被所有对象访问)。

// Create a new User constructor
function User( name, age ){
this.name = name;
this.age = age;
}

// Add a new function to the object prototype
User.prototype.getName = function(){
return this.name;
};

// And add another function to the prototype
// Notice that the context is going to be within
// the instantiated object
User.prototype.getAge = function(){
return this.age;
};

// Instantiate a new User object
var user = new User( "Bob", 44 );

// We can see that the two methods we attached are with the
// object, with proper contexts
alert( user.getName() == "Bob" );
alert( user.getAge() == 44 );

 Simple constructors and simple manipulation of the prototype object is as far as most

JavaScript developers get when building new applications. In the rest of this section I’m going
to explain a couple other techniques that you can use to get the most out of your objectoriented
code.

2私有方法 private method

   私有方法和私有变量只允许其他的私有方法,私有变量和特权方法访问,这种方法可以定义一些只让对象内部访问,而外部访问不到的代码,这一技巧来自于crockford的履历。

// A Object constructor that represents a classroom
function Classroom( students, teacher ) {
// A private method used for display all the students in the class
function disp() {
alert( this.names.join(", ") );
}

// Store the class data as public object properties
this.students = students;
this.teacher = teacher;

// Call the private method to display the error
disp();
}

// Create a new classroom object
var class = new Classroom( [ "John", "Bob" ], "Mr. Smith" );

// Fails, as disp is not a public property of the object
class.disp(); 调用会失败。

 虽然很简单,但私有方法和私有变量在保证代码没有冲突的同时,允许你对用户能使用和能看到的内容有更好的控制。下一步,我们将了解特权方法,它是私有方法和公共方法的混合体。

3.特权方法Privileged Methods
Privileged methods is a term coined by Douglas Crockford to refer to methods that are able
to view and manipulate private variables (within an object) while still being accessible to
users as a public method. Listing 2-24 shows an example of using privileged methods.

 特权方法用来指代那些在查看并处理(对象中)私有变量的同时允许用户以公共方法的方式访问的方法。

就是java的getter方法。

// Create a new User object constructor
function User( name, age ) {
// Attempt to figure out the year that the users was born
var year = (new Date()).getFullYear() – age;

// Create a new Privileged method that has access to
// the year variable, but is still publically available
this.getYearBorn = function(){
return year;
};
}

// Create a new instance of the user object
var user = new User( "Bob", 44 );

// Verify that the year returned is correct
alert( user.getYearBorn() == 1962 );

// And notice that we're not able to access the private year
// property of the object
alert( user.year == null );

 本质上,特权方法是动态生成的,因为他们是运行时才添加到对象中的,而不是在代码第一次编译时就已经生成的,虽然这个技巧比

往对象的prototype上绑定一个简单的方法开销更大,但功能更强大,更灵活,下面展示了动态生成方法能实现的例子:

In essence, privileged methods are dynamically generated methods, because they’re added
to the object at runtime, rather than when the code is first compiled. While this technique is
computationally more expensive than binding a simple method to the object prototype, it is
also much more powerful and flexible. Listing 2-25 is an example of what can be accomplished
using dynamically generated methods.

// Create a new user object that accepts an object of properties
function User( properties ) {
// Iterate through the properties of the object, and make sure
// that it's properly scoped (as discussed previously)
for ( var i in properties ) { (function(){
// Create a new getter for the property
this[ "get" + i ] = function() {
return properties[i];
};

// Create a new setter for the property
this[ "set" + i ] = function(val) {
properties[i] = val;
};
})(); }
}

// Create a new user object instance and pass in an object of
// properties to seed it with
var user = new User({
name: "Bob",
age: 44
});

// Just note that the name property does not exist, as it's private
// within the properties object
alert( user.name == null );

// However, we're able to access its value using the new getname()
// method, that was dynamically generated
alert( user.getname() == "Bob" );

// Finally, we can see that it''s possible to set and get the age using
// the newly generated functions
user.setage( 22 );
alert( user.getage() == 22 );

这上面的代码没有正确设置上下文与闭包调用对象(caller object)的变量,所以不会正确执行。



 
原文地址:https://www.cnblogs.com/youxin/p/2673143.html