JavaScript面向对象精要(上部)


Posted in Javascript onSeptember 12, 2017

数据类型

在JavaScript中,数据类型分为两类:

原始类型

保存一些简单数据,如true,5等。JavaScript共有5中原始类型:

boolean:布尔,值为true或false
number:数字,值为任何整型会浮点数值
string:字符串,值为由单引号或双引号括出的单个字符或连续字符(JavaScript不区分字符类型)
null:空类型,其仅有一个值:nulll
undefined:未定义,其仅有一个值:undefined

var name = "Pomy";
var blog = "http://www.ido321.com";
var age = 22;
alert(typeof blog); //"string"
alert(typeof age); //"number"

原始类型的值是直接保存在变量中,并可以用 typeof 进行检测。但是typeof对null的检测是返回object,而不是返回null:

//弹出Not null
if(typeof null){
  alert("Not null");  
}else{
  alert("null");
}

所以检测null时,最好用全等于(===),其还能避免强制类型转换:

console.log("21" === 21); //false
console.log("21" == 21); //true
console.log(undefined == null); //true
console.log(undefined === null); //false

对于字符串、数字或者布尔值,其都有对应的方法,这些方法来自于对应的原始封装类型:String、Number和Boolean。原始封装类型将被自动创建。

var name = "Pomy";
var char = name.charAt(0);
console.log(char); //"P"

在JavaScript引擎中发生的事情:

var name = "Pomy";
var temp = new String(name);
var char = temp.charAt(0);
temp = null;
console.log(char); //"P"

字符串对象的引用在用完之后立即被销毁,所以不能给字符串添加属性,并且instanceof检测对应类型时均返回false:

var name = "Pomy";
name.age = 21;
console.log(name.age);  //undefined
console.log(name instanceof String); //false

引用类型

保存为对象,实质是指向内存位置的引用,所以不在变量中保存对象。除了自定义的对象,JavaScript提供了6中内建类型:

Array:数组类型,以数字为索引的一组值的有序列表
Date:日期和时间类型
Error:运行期错误类型
Function:函数类型
Object:通用对象类型
RegExp:正则表达式类型
可以用new来实例化每一个对象,或者用字面量形式来创建对象:

var obj = new Object;
var own = {
      name:"Pomy",
      blog:"http://www.ido321.com",
      "my age":22
    };
console.log(own.blog);  //访问属性
console.log(own["my age"]); 
obj = null; //解除引用

obj 并不包含对象实例,而是一个指向内存中实际对象所在位置的指针(或者说引用)。因为typeof对所有非函数的引用类型均返回object,所以需要用instanceof来检测引用类型。

函数

在JavaScript中,函数就是对象。使函数不同于其他对象的决定性特性是函数存在一个被称为[[Call]]的内部属性。内部属性无法通过代码访问而是定义了代码执行时的行为。

创建形式

1、函数声明:用function关键字,会被提升至上下文
2、函数表达式:不能被提升
3、实例化Function内建类型

sayHi();  //函数提升
function sayHi(){
  console.log("Hello");
}
//其他等效等效方式
/*
var sayHi = function(){
   console.log("Hello");
}
var sayHi = new Function(" console.log(\"Hello\");");
*/

参数

JavaScript函数的另外一个独特之处在于可以给函数传递任意数量的参数。函数参数被保存在arguments类数组对象中,其自动存在函数中,能通过数字索引来引用参数,但它不是数组实例:

alert(Array.isArray(arguments));   //false
类数组对象arguments 保存的是函数的实参,但并不会忽略形参。因而,arguments.length返回实参列表的长度,arguments.callee.length返回形参列表的长度。

function ref(value){
  return value;
}
console.log(ref("Hi"));
console.log(ref("Hi",22));
console.log(ref.length); //1

函数中的this

关于this的问题,可参考此文:JavaScript中的this。
JavaScript提供了三个方法用于改变this的指向:call、apply和bind。三个函数的第一个参数都是指定this的值,其他参数均作为参数传递给函数。

对象

对象是一种引用类型,创建对象常见的两种方式:Object构造函数和对象字面量形式:

var per1 = {
  name:"Pomy",
  blog:"http://www.ido321.com"
};
var per2 = new Object;
per2.name = "不写代码的码农";

属性操作

在JavaScript中,可以随时为对象添加属性:

per1.age = 0;
per1.sayName = function(){
  alert(this.name);  //"Pomy"
}

因而,在检测对象属性是否存在时,常犯的一个错误是:

//结果是false
if(per1.age){
  alert(true)
}else{
  alert(false);
}

per1.age 是存在的,但是其值是0,所以不能满足if条件。if判断中的值是一个对象、非空字符串、非零数字或true时,判断会评估为真;而当值是一个null、undefined、0、false、NaN或空字符串时评估为假。

因而,检测属性是否存在时,有另外的两种方式:in和hasOwnProperty(),前者会检测原型属性和自有(实例)属性,后者只检测自有(实例)属性。

console.log("age" in per1); //true
console.log(per1.hasOwnProperty("age")); //true
console.log("toString" in per1); //true
console.log(per1.hasOwnProperty("toString")); //false

对象per1并没有定义toString,该属性继承于Object.prototype,所以in和hasOwnProperty()检测该属性时出现差异。如果只想判断一个对象属性是不是原型,可以利用如下方法:

function isPrototypeProperty(obj,name){
  return name in obj && !obj.hasOwnProperty(name);
}

若要删除一个属性,用delete操作符,用于删除自有属性,不能删除原型属性。

per1.toString = function(){
  console.log("per1对象");
};
console.log(per1.hasOwnProperty("toString"));  //true
per1.toString();  //"per1对象"
delete per1.toString;
console.log(per1.hasOwnProperty("toString"));  //false
console.log(per1.toString()); //[object Object]

有时需要枚举对象的可枚举属性,也有两种方式:for-in循环和Object.keys(),前者依旧会遍历出原型属性,后者只返回自有属性。所有可枚举属性的内部属性[[Enumerable]]的值均为true。

var per3 = {
  name:"Pomy",
  blog:"http://www.ido321.com",
  age:22,
  getAge:function(){
    return this.age;
  }
};

实际上,大部分原生属性的[[Enumerable]]的值均为false,即该属性不能枚举。可以通过propertyIsEnumerable()检测属性是否可以枚举:

console.log(per3.propertyIsEnumerable("name")); //true
var pros = Object.keys(per3); //返回可枚举属性的名字数组
console.log("length" in pros); //true
console.log(pros.propertyIsEnumerable("length")); //false

属性name是自定义的,可枚举;属性length是Array.prototype的内建属性,不可枚举。

属性类型

属性有两种类型:数据属性和访问器属性。二者均具有四个属性特征:

数据属性:[[Enumerable]]、[[Configurable]]、[[Value]]和[[Writable]]
访问器属性:[[Enumerable]]、[[Configurable]]、[[Get]]和[[Set]]
**[[Enumerable]] :**布尔值,属性是否可枚举,自定义属性默认是true。
**[[Configurable]] :**布尔值,属性是否可配置(可修改或可删除),自定义属性默认是true。它是不可逆的,即设置成false后,再设置成true会报错。
**[[Value]]:**保存属性的值。
**[[Writable]]:**布尔值,属性是否可写,所有属性默认可写。
**[[Get]]:**获取属性值。
**[[Set]]:**设置属性值。

ES 5提供了两个方法用于设置这些内部属性:
Object.defineProperty(obj,pro,desc_map) 和 Object.defineProperties(obj,pro_map)。利用这两个方法为per3添加一个属性和创建一个新对象per4:

Object.defineProperty(per3,"sex",{
  value:"male",
  enumerable:false,
  configurable:false, //属性不能删除和修改,该值也不能被设置成true
});
console.log(per3.sex); //'male'
console.log(per3.propertyIsEnumerable("sex")); //false
delete per3.sex;  //不能删除
per3.sex = "female"; //不能修改
console.log(per3.sex); //'male'
Object.defineProperty(per3,"sex",{
  configurable:true, //报错
});
per4 = {};
Object.defineProperties(per4,{
  name:{
    value:"dwqs",
    writable:true
  },
  blog:{
    value:"http://blog.92fenxiang.com"
  },
  Name:{
    get:function(){
      return this.name;
    },
    set:function(value){
      this.name = value;
    },
    enumerable:true,
    configurable:true
  }
});
console.log(per4.name); //dwqs
per4.Name = "Pomy";
console.log(per4.Name); //Pomy

需要注意的是,通过这两种方式来定义新属性时,如果不指定特征值,则默认是false,也不能创建同时具有数据特征和访问器特征的属性。可以通过Object.getOwnPropertyDescriptor()方法来获取属性特征的描述,接受两个参数:对象和属性名。若属性存在,则返回属性描述对象。

var desc = Object.getOwnPropertyDescriptor(per4,"name");
console.log(desc.enumerable); //false
console.log(desc.configurable); //false
console.log(desc.writable); //true

根据属性的属性类型,返回的属性描述对象包含其对应的四个属性特征。

禁止修改对象

对象和属性一样具有指导其行为的内部特征。其中,[[Extensible]]是一个布尔值,指明改对象本身是否可以被修改([[Extensible]]值为true)。创建的对象默认都是可以扩展的,可以随时添加新的属性。
ES5提供了三种方式:

Object.preventExtensions(obj):创建不可扩展的obj对象,可以利用Object.isExtensible(obj)来检测obj是否可以扩展。严格模式下给不扩展对象添加属性会报错,非严格模式下则添加失败。
Object.seal(obj):封印对象,此时obj的属性变成只读,不能添加、改变或删除属性(所有属性都不可配置),其[[Extensible]]值为false,[[Configurable]]值为false。可以利用Object.isSealed(obj)来检测obj是否被封印。
Object.freeze(obj):冻结对象,不能在冻结对象上添加或删除属性,不能改变属性类型,也不能写入任何数据类型。可以利用Object.isFrozen(obj)来检测obj是否被冻结。
注意:冻结对象和封印对象均要在严格模式下使用。

"use strict";
var per5 = {
  name:"Pomy"
};
console.log(Object.isExtensible(per5));  //true
console.log(Object.isSealed(per5));     //false
console.log(Object.isFrozen(per5));    //false
Object.freeze(per5);
console.log(Object.isExtensible(per5));  //false
console.log(Object.isSealed(per5));    //true
console.log(Object.isFrozen(per5));    //true
per5.name="dwqs";
console.log(per5.name);  //"Pomy"
per5.Hi = function(){
  console.log("Hi");
};
console.log("Hi" in per5); //false
delete per5.name;
console.log(per5.name); //"Pomy"
var desc = Object.getOwnPropertyDescriptor(per5,"name");
console.log(desc.configurable); //false
console.log(desc.writable); //false

注意,禁止修改对象的三个方法只对对象的自有属性有效,对原型对象的属性无效,仍然可以在原型上添加或修改属性。

function Person(name){
  this.name = name;
}
var person1 = new Person("Pomy");
var person2 = new Person("dwqs");
Object.freeze(person1);
Person.prototype.Hi = function(){
  console.log("Hi");
};
person1.Hi(); //"Hi";
person2.Hi(); //"Hi";

补充:

Object.seal(obj):封印对象,此时obj的属性变成只读,不能添加、改变或删除属性(所有属性都不可配置),其[[Extensible]]值为false,[[Configurable]]值为false。可以利用Object.isSealed(obj)来检测obj是否被封印。
这容易让人歧义,一般说属性,我们都很容易理解为其值。
,希望楼主稍微修改一下,让人更加容易理解。
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal
我更推荐的是倒过来理解
Object.freeze 冻结对象,不能进行任何属性和属性值的增删改。
Object.seal 封闭对象,和Object.freeze相比,可以修改已存在属性的值。

Javascript 相关文章推荐
javascript 禁止复制网页
Jun 11 Javascript
js prototype截取字符串函数
Apr 01 Javascript
多次注册事件会导致一个事件被触发多次的解决方法
Aug 12 Javascript
jQuery拖拽 & 弹出层 介绍与示例
Dec 27 Javascript
jQuery grep()方法详解及实例代码
Oct 30 Javascript
JavaScript递归函数解“汉诺塔”算法代码解析
Jul 05 Javascript
原生JS实现动态加载js文件并在加载成功后执行回调函数的方法
Dec 30 Javascript
js前端如何写一个精确的倒计时代码
Oct 25 Javascript
js实现录音上传功能
Nov 22 Javascript
Element Cascader 级联选择器的使用示例
Jul 27 Javascript
OpenLayers3实现地图显示功能
Sep 25 Javascript
Ant Design的Table组件去除
Oct 24 Javascript
JS库之ParticlesJS使用简介
Sep 12 #Javascript
关于预加载InstantClick的问题解决方法
Sep 12 #Javascript
提升页面加载速度的插件InstantClick
Sep 12 #Javascript
详解如何让InstantClick兼容MathJax、百度统计等
Sep 12 #Javascript
使用InstantClick.js让页面提前加载200ms
Sep 12 #Javascript
VueJs组件prop验证简单介绍
Sep 12 #Javascript
js字符限制(字符截取) 一个中文汉字算两个字符
Sep 12 #Javascript
You might like
PHP 计算代码执行耗时的代码修正网上普遍错误
2011/05/14 PHP
php判断邮箱地址是否存在的方法
2016/02/13 PHP
基于jquery实现的鼠标拖拽元素复制并写入效果
2011/08/23 Javascript
js页面滚动时层智能浮动定位实现(jQuery/MooTools)
2011/08/23 Javascript
解析jquery获取父窗口的元素
2013/06/26 Javascript
Javascript实现页面跳转的几种方式分享
2013/10/26 Javascript
jquery slibings选取同级其他元素的实现代码
2013/11/15 Javascript
jquery html动态生成select标签出问题的解决方法
2013/11/20 Javascript
父页面显示遮罩层弹出半透明状态的dialog
2014/03/04 Javascript
详解jQuery向动态生成的内容添加事件响应jQuery live()方法
2015/11/02 Javascript
node.js实现爬虫教程
2020/08/25 Javascript
jQuery Mobile页面返回不需要重新get
2016/04/26 Javascript
使用jQuery ajaxupload插件实现无刷新上传文件
2017/04/23 jQuery
Vue框架中正确引入JS库的方法介绍
2017/07/30 Javascript
javascript和php使用ajax通信传递JSON的实例
2018/08/21 Javascript
解决vue-cli项目打包出现空白页和路径错误的问题
2018/09/04 Javascript
简单两步使用node发送qq邮件的方法
2019/03/01 Javascript
Vue项目实现简单的权限控制管理功能
2019/07/17 Javascript
手把手带你入门微信小程序新框架Kbone的使用
2020/02/25 Javascript
微信小程序实现上传多张图片、删除图片
2020/07/29 Javascript
vue 点击其他区域关闭自定义div操作
2020/07/17 Javascript
tornado框架blog模块分析与使用
2013/11/21 Python
让Python代码更快运行的5种方法
2015/06/21 Python
深入理解Python3中的http.client模块
2017/03/29 Python
在Qt5和PyQt5中设置支持高分辨率屏幕自适应的方法
2019/06/18 Python
django mysql数据库及图片上传接口详解
2019/07/18 Python
PyTorch笔记之scatter()函数的使用
2020/02/12 Python
Python爬虫实战案例之爬取喜马拉雅音频数据详解
2020/12/07 Python
韩国三星集团旗下时尚品牌官网:SSF SHOP
2016/08/02 全球购物
日本化妆品植村秀俄罗斯官方网站:Shu Uemura俄罗斯
2020/02/01 全球购物
应届毕业生求职自荐书
2014/01/03 职场文书
评析教师个人的自我评价
2014/02/19 职场文书
个人委托书如何写
2014/09/25 职场文书
求职自荐信范文(优秀篇)
2015/03/27 职场文书
2016党校学习心得体会
2016/01/07 职场文书
Python实现视频中添加音频工具详解
2021/12/06 Python