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 相关文章推荐
得到文本框选中的文字,动态插入文字的js代码
Mar 07 Javascript
JS判断页面加载状态以及添加遮罩和缓冲动画的代码
Oct 11 Javascript
关于JavaScript中string 的replace
Apr 12 Javascript
select多选 multiple的使用示例
Jun 16 Javascript
jQuery中ready事件用法实例
Jan 19 Javascript
js判断复选框是否选中及选中个数的实现代码
May 30 Javascript
JavaScript实现打开链接页面的方式汇总
Jun 02 Javascript
详解AngularJS2 Http服务
Jun 26 Javascript
vue2利用Bus.js如何实现非父子组件通信详解
Aug 25 Javascript
Javascript中parseInt的正确使用方式
Oct 17 Javascript
转换layUI的数据表格中的日期格式方法
Sep 19 Javascript
详解JavaScript 事件流
Sep 02 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产生随机字符串函数
2006/12/06 PHP
PHP基础知识介绍
2013/09/17 PHP
ThinkPHP3.1新特性之多层MVC的支持
2014/06/19 PHP
Js 本页面传值实现代码
2009/05/17 Javascript
JQuery 将元素显示在屏幕的中央的代码
2010/02/27 Javascript
纯CSS打造的导航菜单(附jquery版)
2010/08/07 Javascript
《JavaScript高级程序设计》阅读笔记(三) ECMAScript中的引用类型
2012/02/27 Javascript
jquery validate demo 基础
2015/10/29 Javascript
react-native中ListView组件点击跳转的方法示例
2017/09/30 Javascript
vue.js中created方法作用
2018/03/30 Javascript
AngularJS中的作用域实例分析
2018/05/16 Javascript
js中int和string数据类型互相转化实例
2019/01/16 Javascript
Vue.js + Nuxt.js 项目中使用 Vee-validate 表单校验
2019/04/22 Javascript
JS校验与最终登陆界面功能完整示例
2020/01/13 Javascript
Python字符转换
2008/09/06 Python
python登录QQ邮箱发信的实现代码
2013/02/10 Python
使用Python编写一个在Linux下实现截图分享的脚本的教程
2015/04/24 Python
Python入门_浅谈逻辑判断与运算符
2017/05/16 Python
在Python中表示一个对象的方法
2019/06/25 Python
python GUI编程(Tkinter) 创建子窗口及在窗口上用图片绘图实例
2020/03/04 Python
基于python生成英文版词云图代码实例
2020/05/16 Python
python读取excel进行遍历/xlrd模块操作
2020/07/12 Python
python上下文管理器异常问题解决方法
2021/02/07 Python
介绍一下Linux文件的记录形式
2012/04/18 面试题
工作表现自我评价
2014/02/08 职场文书
财产公证书样本
2014/04/04 职场文书
2014年秋季开学寄语
2014/08/02 职场文书
2014党员民主评议个人总结
2014/09/10 职场文书
大学生联谊活动策划书(光棍节)
2014/10/10 职场文书
中秋客户感谢信
2015/01/22 职场文书
学校捐书活动总结
2015/05/08 职场文书
2016年教师师德师风心得体会
2016/01/12 职场文书
XX部保密工作制度范本
2019/08/27 职场文书
Python人工智能之混合高斯模型运动目标检测详解分析
2021/11/07 Python
MySQL 分区表中分区键为什么必须是主键的一部分
2022/03/17 MySQL
我家女友可不止可爱呢 公开OP主题曲无字幕动画MV
2022/04/11 日漫